From c5c3b3a16ea5ab070e47430a09c1488458d17b30 Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 15 Sep 2026 15:04:25 -0500 Subject: [PATCH 1/2] [FIX] Ruff fixes. --- .github/scripts/type_completeness.py | 11 +- ARCHITECTURE.md | 6 +- chainladder/adjustments/__init__.py | 2 +- chainladder/adjustments/tests/test_trend.py | 9 +- chainladder/core/io.py | 26 +- chainladder/core/slice.py | 10 + chainladder/core/tests/test_correlation.py | 9 +- chainladder/core/tests/test_slicing.py | 42 + chainladder/core/typing.py | 52 +- chainladder/development/outstanding.py | 2 +- .../development/tests/test_outstanding.py | 48 +- chainladder/methods/bornferg.py | 12 +- chainladder/methods/chainladder.py | 10 +- chainladder/methods/expectedloss.py | 11 +- chainladder/tails/__init__.py | 4 +- chainladder/tails/clark.py | 24 +- chainladder/tails/tests/test_bondy.py | 5 +- chainladder/tails/tests/test_constant.py | 4 +- chainladder/tails/tests/test_exponential.py | 15 +- chainladder/utils/__init__.py | 7 +- chainladder/workflow/__init__.py | 7 +- chainladder/workflow/gridsearch.py | 42 +- chainladder/workflow/tests/test_predict.py | 13 +- docs/_ext/tests/test_ext_linkcode.py | 13 +- docs/conf.py | 104 +- docs/friedland/chapter_10.ipynb | 224 +- docs/friedland/chapter_7_part_2.ipynb | 887 +- docs/friedland/chapter_8.ipynb | 10311 ++++++++-------- docs/gallery/plot_berqsherm_case.ipynb | 29 +- docs/gallery/plot_bf_apriori_from_cl.ipynb | 19 +- docs/gallery/plot_callable_dev_constant.ipynb | 28 +- docs/gallery/plot_clarkldf.ipynb | 17 +- docs/gallery/plot_exponential_smoothing.ipynb | 18 +- docs/gallery/plot_glm_ldf.ipynb | 15 +- docs/gallery/plot_ibnr_runoff.ipynb | 19 +- docs/gallery/plot_industry_to_company.ipynb | 43 +- docs/gallery/plot_tailcurve_compare.ipynb | 18 +- docs/gallery/plot_triangle_slicing.ipynb | 19 +- .../sandbox_workbook_filled.ipynb | 8 +- docs/prep_sphinx_conf.py | 1 + docs/user_guide/utilities.ipynb | 14 +- 41 files changed, 6534 insertions(+), 5624 deletions(-) diff --git a/.github/scripts/type_completeness.py b/.github/scripts/type_completeness.py index 4234c6837..60a7e727a 100644 --- a/.github/scripts/type_completeness.py +++ b/.github/scripts/type_completeness.py @@ -2,21 +2,19 @@ Builds a Markdown summary comparing pyright `--verifytypes` reports for a PR's base and head commits, for posting as a PR comment. """ + from __future__ import annotations import argparse import json from pathlib import Path -from typing import ( - Any, - Literal -) +from typing import Any, Literal # Decorates the patch coverage table. STATUS_ICON = {"known": "✅", "ambiguous": "⚠️", "unknown": "❌"} -def status_of(symbol: dict[str, Any]) -> Literal['known', 'ambiguous', 'unknown']: +def status_of(symbol: dict[str, Any]) -> Literal["known", "ambiguous", "unknown"]: """ Maps the --verifytypes JSON boolean flags, isTypeKnown and isTypeAmbiguous, to internal representation in script: known, ambiguous, and unknown. @@ -306,7 +304,8 @@ def build_summary(base_path: Path, head_path: Path, run_url: str | None = None) if removed_names: parts.append(f"{len(removed_names)} no longer exported") sections += [ - "**Patch (exported symbols added or changed by this PR):** " + "; ".join(parts), + "**Patch (exported symbols added or changed by this PR):** " + + "; ".join(parts), "", render_counts_table([("Patch", patch_counts)]), "", diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 08a3369a9..5ea6ab962 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -166,10 +166,12 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: from chainladder.core.typing import TriangleProtocol + _MixinBase = TriangleProtocol else: _MixinBase = object + class TriangleMixin(_MixinBase): # Pyright sees TriangleProtocol as the base — self has .shape, .values, .sum, etc. # At runtime the base is object — no Protocol stubs in the MRO. @@ -191,8 +193,8 @@ if TYPE_CHECKING: from chainladder import Triangle from chainladder.core.typing import TriangleProtocol -def transform(X: TriangleProtocol) -> Triangle: - ... + +def transform(X: TriangleProtocol) -> Triangle: ... ``` - **Input typed as `TriangleProtocol`**: accepts any object that structurally satisfies the protocol (a real `Triangle`, a mock in tests, a future subclass) without requiring a concrete import. diff --git a/chainladder/adjustments/__init__.py b/chainladder/adjustments/__init__.py index ad3a660b8..f19975c80 100644 --- a/chainladder/adjustments/__init__.py +++ b/chainladder/adjustments/__init__.py @@ -11,5 +11,5 @@ "ParallelogramOLF", "Trend", "TrendConstant", - "DisposalRate" + "DisposalRate", ] diff --git a/chainladder/adjustments/tests/test_trend.py b/chainladder/adjustments/tests/test_trend.py index 18c1ba0ca..1b6cbc9c0 100644 --- a/chainladder/adjustments/tests/test_trend.py +++ b/chainladder/adjustments/tests/test_trend.py @@ -5,12 +5,14 @@ def test_trend1(clrd): tri = clrd[["CumPaidLoss", "EarnedPremDIR"]].sum() lhs = ( - cl.CapeCod(0.05) + cl + .CapeCod(0.05) .fit(tri["CumPaidLoss"], sample_weight=tri["EarnedPremDIR"].latest_diagonal) .ibnr_ ) rhs = ( - cl.CapeCod() + cl + .CapeCod() .fit( cl.Trend(0.05).fit_transform(tri["CumPaidLoss"]), sample_weight=tri["EarnedPremDIR"].latest_diagonal, @@ -24,7 +26,8 @@ def test_trend2(raa): tri = raa assert ( abs( - cl.Trend( + cl + .Trend( trends=[0.05, 0.05], dates=[(None, "1985"), ("1985", None)], axis="origin", diff --git a/chainladder/core/io.py b/chainladder/core/io.py index 6f6836a68..7162fe3c1 100644 --- a/chainladder/core/io.py +++ b/chainladder/core/io.py @@ -1,6 +1,7 @@ """ Support Triangle I/O capabilities. """ + # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at https://mozilla.org/MPL/2.0/. @@ -13,7 +14,7 @@ class TriangleIO: def to_pickle(self, path, protocol=None): - """ Serializes triangle object to pickle. + """Serializes triangle object to pickle. Parameters ---------- @@ -53,7 +54,7 @@ def to_pickle(self, path, protocol=None): dill.dump(self, pkl) def to_json(self): - """ Serializes triangle object to json format + """Serializes triangle object to json format Returns ------- @@ -88,8 +89,13 @@ def to_json(self): "is_pattern": self.is_pattern, "columns": list(self.columns), } - out = self.cum_to_incr().dev_to_val().to_frame( - keepdims=True, origin_as_datetime=True).fillna(0) + out = ( + self + .cum_to_incr() + .dev_to_val() + .to_frame(keepdims=True, origin_as_datetime=True) + .fillna(0) + ) x = out.reset_index().to_json(orient="split", date_unit="ns") json_dict = {"metadata": json.dumps(metadata), "data": x} sub_tris = [k for k, v in vars(self).items() if isinstance(v, TriangleIO)] @@ -99,17 +105,17 @@ def to_json(self): dfs = [k for k, v in vars(self).items() if isinstance(v, pd.DataFrame)] json_dict["dfs"] = {df: getattr(self, df).to_json() for df in dfs} dfs = [k for k, v in vars(self).items() if isinstance(v, pd.Series)] - json_dict["dfs"].update( - {df: getattr(self, df).to_frame().to_json() for df in dfs} - ) + json_dict["dfs"].update({ + df: getattr(self, df).to_frame().to_json() for df in dfs + }) return json.dumps(json_dict) class EstimatorIO: - """ Class intended to allow persistence of estimator objects """ + """Class intended to allow persistence of estimator objects""" def to_pickle(self, path, protocol=None): - """ Serializes triangle object to pickle. + """Serializes triangle object to pickle. Parameters ---------- @@ -149,7 +155,7 @@ def to_pickle(self, path, protocol=None): dill.dump(self, pkl) def to_json(self): - """ Serializes triangle object to json format + """Serializes triangle object to json format Returns ------- diff --git a/chainladder/core/slice.py b/chainladder/core/slice.py index 984b75865..d3d2e9040 100644 --- a/chainladder/core/slice.py +++ b/chainladder/core/slice.py @@ -562,6 +562,16 @@ def __setitem__( ------- None """ + # Case full slice, e.g. tri[:] = value: mirror pandas' df[:] = value by + # broadcasting across every cell rather than treating ":" as a column label. + if isinstance(key, slice): + if key == slice(None, None, None): + self.iloc[:] = value + return + raise TypeError( + "Partial slicing is not supported for Triangle column assignment. " + "Use tri.iloc[...] or tri.loc[...] to set values by position or label." + ) xp: ModuleType = self.get_array_module() # Case callable, create lazy-eval virtual columns, but do not compute. if callable(value): diff --git a/chainladder/core/tests/test_correlation.py b/chainladder/core/tests/test_correlation.py index 8b10a460f..b72364be8 100644 --- a/chainladder/core/tests/test_correlation.py +++ b/chainladder/core/tests/test_correlation.py @@ -3,18 +3,23 @@ raa = cl.load_sample("RAA") + def test_val_corr_total_true(): assert raa.valuation_correlation(p_critical=0.5, total=True) + def test_val_corr_total_false(): assert raa.valuation_correlation(p_critical=0.5, total=False) + def test_dev_corr(): assert raa.development_correlation(p_critical=0.5) + def test_dev_corr_sparse(): - assert raa.set_backend('sparse').development_correlation(p_critical=0.5) + assert raa.set_backend("sparse").development_correlation(p_critical=0.5) + def test_validate_critical(): with pytest.raises(ValueError): - raa.valuation_correlation(p_critical=1.5, total=True) \ No newline at end of file + raa.valuation_correlation(p_critical=1.5, total=True) diff --git a/chainladder/core/tests/test_slicing.py b/chainladder/core/tests/test_slicing.py index acb579a4e..5f371c5ca 100644 --- a/chainladder/core/tests/test_slicing.py +++ b/chainladder/core/tests/test_slicing.py @@ -689,6 +689,48 @@ def test_setitem_existing_column_array_value(raa: Triangle) -> None: assert tri["values"] == raa["values"] * 3 +def test_setitem_full_slice_broadcasts_value(raa: Triangle) -> None: + """ + Assigning through a bare full slice, e.g. tri[:] = 0, should broadcast the + value across every cell like pandas' df[:] = value, rather than being + misread as a column label. + + Parameters + ---------- + raa: Triangle + The raa sample data set fixture. + + Returns + ------- + None + """ + tri = raa.copy() + if tri.array_backend == "sparse": + pytest.skip("Test is specific to the numpy backend.") + tri[:] = 0 + assert list(tri.columns) == list(raa.columns) + assert np.nansum(tri.values) == 0 + + +def test_setitem_partial_slice_raises(raa: Triangle) -> None: + """ + Assigning through a partial slice key is ambiguous for column assignment + and should raise rather than silently creating a bogus column. + + Parameters + ---------- + raa: Triangle + The raa sample data set fixture. + + Returns + ------- + None + """ + tri = raa.copy() + with pytest.raises(TypeError): + tri[1:3] = 0 + + def test_sparse_column_assignment(prism): t = prism.copy() out = t["Paid"] diff --git a/chainladder/core/typing.py b/chainladder/core/typing.py index ae0405193..d23e39933 100644 --- a/chainladder/core/typing.py +++ b/chainladder/core/typing.py @@ -11,7 +11,7 @@ Protocol, # Self, # Make use of this once Python 3.10 is deprecated. TYPE_CHECKING, - TypeAlias + TypeAlias, ) if TYPE_CHECKING: @@ -25,7 +25,7 @@ Ilocation, Location, TriangleSlicer, - VirtualColumns + VirtualColumns, ) from numpy.typing import ArrayLike from pandas import DataFrame, Series @@ -49,10 +49,12 @@ # before index_key/other_key resolve it to a positional _AxisKey. _LabelKey: TypeAlias = IndexExpression | str | pd.Series | pd.DataFrame + class TriangleProtocol(Protocol): """ Common interface expected for Triangle mixins. """ + @property def shape(self) -> tuple[int, int, int, int]: ... @@ -96,7 +98,9 @@ def nan_triangle(self) -> BackendArray: ... def __len__(self) -> int: ... def get_array_module(self, arr: ArrayLike | None = None) -> ModuleType: ... def copy(self) -> Triangle: ... - def set_backend(self, backend: str, inplace: bool = False, **kwargs) -> Triangle: ... + def set_backend( + self, backend: str, inplace: bool = False, **kwargs + ) -> Triangle: ... def drop( self, labels: str | int | list | None = None, @@ -108,7 +112,9 @@ def drop( ) -> Triangle: ... def val_to_dev(self) -> Triangle: ... def _repr_format(self, origin_as_datetime: bool = False) -> pd.DataFrame: ... - def _slice(self, key: pd.Series | np.ndarray, axis: Literal['ddims', 'odims']) -> Triangle: ... + def _slice( + self, key: pd.Series | np.ndarray, axis: Literal["ddims", "odims"] + ) -> Triangle: ... def _slice_valuation(self, key: np.ndarray) -> Triangle: ... def to_frame( self, @@ -116,16 +122,36 @@ def to_frame( keepdims: bool = False, implicit_axis: bool = False, ) -> DataFrame | Series: ... - def sum(self, axis: str | int | None = None, *args, **kwargs) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def fillna(self, value: int | float | ndarray, inplace: bool = False) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def fillzero(self, inplace: bool = False) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def __round__(self, ndigits: int = 0) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def __add__(self, other: Any) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def __radd__(self, other: Any) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def __mul__(self, other: Any) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. - def __rmul__(self, other: Any) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def sum( + self, axis: str | int | None = None, *args, **kwargs + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def fillna( + self, value: int | float | ndarray, inplace: bool = False + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def fillzero( + self, inplace: bool = False + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def __round__( + self, ndigits: int = 0 + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def __add__( + self, other: Any + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def __radd__( + self, other: Any + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def __mul__( + self, other: Any + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. + def __rmul__( + self, other: Any + ) -> TriangleProtocol: ... # -> Self once Python 3.10 is deprecated. @overload def __getitem__(self, key: pd.Series | np.ndarray | list[str]) -> Triangle: ... @overload def __getitem__(self, key: str | int) -> Triangle | Series: ... - def __setitem__(self, key: str | int, value: int | float | TriangleSlicer | Callable[[Triangle], TriangleSlicer]) -> None: ... \ No newline at end of file + def __setitem__( + self, + key: str | int, + value: int | float | TriangleSlicer | Callable[[Triangle], TriangleSlicer], + ) -> None: ... diff --git a/chainladder/development/outstanding.py b/chainladder/development/outstanding.py index 4099eca0f..5b9bc1094 100644 --- a/chainladder/development/outstanding.py +++ b/chainladder/development/outstanding.py @@ -11,7 +11,7 @@ class CaseOutstanding(DevelopmentBase): - """ Deterministic development from prior-lag case reserves. + """Deterministic development from prior-lag case reserves. Estimates incremental paid amounts and case-reserve runoff as fractions of the prior lag's carried case reserve. Like diff --git a/chainladder/development/tests/test_outstanding.py b/chainladder/development/tests/test_outstanding.py index 8410f68b0..efe5fbbfe 100644 --- a/chainladder/development/tests/test_outstanding.py +++ b/chainladder/development/tests/test_outstanding.py @@ -19,41 +19,37 @@ def test_outstanding_friedland_example(): paid_to_incurred=("paid", "incurred"), paid_n_periods=3, case_n_periods=3 ).fit(usauto) - expected_paid_ldf = np.array( + expected_paid_ldf = np.array([ [ - [ - 0.833, - 0.701, - 0.714, - 0.714, - 0.653, - 0.631, - 0.553, - 0.437, - 0.524, - ] + 0.833, + 0.701, + 0.714, + 0.714, + 0.653, + 0.631, + 0.553, + 0.437, + 0.524, ] - ) + ]) assert ( model.paid_ldf_.to_frame(origin_as_datetime=False).values - expected_paid_ldf < 0.001 ).all() - expected_case_ldf = np.array( + expected_case_ldf = np.array([ [ - [ - 0.526, - 0.566, - 0.528, - 0.486, - 0.511, - 0.555, - 0.652, - 0.674, - 0.580, - ] + 0.526, + 0.566, + 0.528, + 0.486, + 0.511, + 0.555, + 0.652, + 0.674, + 0.580, ] - ) + ]) assert ( model.case_ldf_.to_frame(origin_as_datetime=False).values - expected_case_ldf < 0.001 diff --git a/chainladder/methods/bornferg.py b/chainladder/methods/bornferg.py index 6dca40ac2..e160376a1 100644 --- a/chainladder/methods/bornferg.py +++ b/chainladder/methods/bornferg.py @@ -12,11 +12,11 @@ class BornhuetterFerguson(Benktander): apriori: float, optional (default=1.0) Multiplier for the `sample_weight` used in the Bornhuetter Ferguson method. If `sample_weight` is already an apriori measure of ultimate, - then use 1.0. - The recommended pratice is to seperate the model parameter assumption + then use 1.0. + The recommended pratice is to seperate the model parameter assumption and data apart. - For example, if the apriori s 80% of premium, it is recommended to set - the aprior as 0.8 and leave the premium data in `sample_weight` argument + For example, if the apriori s 80% of premium, it is recommended to set + the aprior as 0.8 and leave the premium data in `sample_weight` argument unmodified. apriori_sigma: float, optional (default=0.0) Standard deviation of the apriori. When used in conjunction with the @@ -128,7 +128,7 @@ def fit(self, X, y=None, sample_weight=None): Fit returns the estimator itself, with ``ultimate_`` populated. .. testsetup:: - + import chainladder as cl .. testcode:: @@ -167,7 +167,7 @@ def predict(self, X, sample_weight=None): current Triangle and a refreshed apriori. .. testsetup:: - + import chainladder as cl .. testcode:: diff --git a/chainladder/methods/chainladder.py b/chainladder/methods/chainladder.py index 5cbc77941..7212c7367 100644 --- a/chainladder/methods/chainladder.py +++ b/chainladder/methods/chainladder.py @@ -131,7 +131,7 @@ def fit(self, X, y=None, sample_weight=None): attribute access. .. testsetup:: - + import chainladder as cl .. testcode:: @@ -142,7 +142,7 @@ def fit(self, X, y=None, sample_weight=None): .. testoutput:: Chainladder() - + """ super().fit(X, y, sample_weight) self.ultimate_ = self._get_ultimate(self.X_) @@ -199,9 +199,9 @@ def predict(self, X, sample_weight=None): return X_new def _get_ultimate(self, X, sample_weight=None): - """ Private method that uses CDFs to obtain an ultimate vector """ + """Private method that uses CDFs to obtain an ultimate vector""" ld = X.incr_to_cum().latest_diagonal ultimate = X.incr_to_cum().copy() - cdf = self._align_cdf(ultimate, sample_weight) - ultimate = ld * cdf + cdf = self._align_cdf(ultimate, sample_weight) + ultimate = ld * cdf return self._set_ult_attr(ultimate) diff --git a/chainladder/methods/expectedloss.py b/chainladder/methods/expectedloss.py index 16b412b29..38d7ca136 100644 --- a/chainladder/methods/expectedloss.py +++ b/chainladder/methods/expectedloss.py @@ -5,8 +5,8 @@ class ExpectedLoss(Benktander): - """The deterministic Expected Loss IBNR model, it ignores all data in the - triangle, and only uses the sample_weight modified by the apriori to + """The deterministic Expected Loss IBNR model, it ignores all data in the + triangle, and only uses the sample_weight modified by the apriori to calculate the ultimate losses. Parameters @@ -51,7 +51,7 @@ class ExpectedLoss(Benktander): .. testcode:: xyz = cl.load_sample("xyz") - + ibnr = ( cl.ExpectedLoss() .fit(X=xyz["Paid"], sample_weight=xyz["Premium"].latest_diagonal) @@ -60,7 +60,7 @@ class ExpectedLoss(Benktander): print(ibnr) .. testoutput:: - + 2261 1998 4178.0 1999 6683.0 @@ -88,7 +88,7 @@ class ExpectedLoss(Benktander): print(ibnr) .. testoutput:: - + 2261 1998 2178.0 1999 3533.0 @@ -145,4 +145,3 @@ def predict(self, X, sample_weight=None): Loss data with Bornhuetter-Ferguson ultimate applied """ return super().predict(X, sample_weight) - diff --git a/chainladder/tails/__init__.py b/chainladder/tails/__init__.py index f4b09df8b..c192a6d7c 100644 --- a/chainladder/tails/__init__.py +++ b/chainladder/tails/__init__.py @@ -1,5 +1,5 @@ -""" tails should store all tail methodologies -""" +"""tails should store all tail methodologies""" + from chainladder.tails.base import TailBase # noqa (API import) from chainladder.tails.constant import TailConstant # noqa (API import) from chainladder.tails.curve import TailCurve # noqa (API import) diff --git a/chainladder/tails/clark.py b/chainladder/tails/clark.py index 841afc8ef..a23c85d1a 100644 --- a/chainladder/tails/clark.py +++ b/chainladder/tails/clark.py @@ -112,8 +112,13 @@ class TailClark(TailBase): """ - def __init__(self, growth="loglogistic", truncation_age=None, - attachment_age=None, projection_period=12): + def __init__( + self, + growth="loglogistic", + truncation_age=None, + attachment_age=None, + projection_period=12, + ): self.growth = growth self.truncation_age = truncation_age self.attachment_age = attachment_age @@ -155,10 +160,13 @@ def fit(self, X, y=None, sample_weight=None): ) fitted = xp.repeat(fitted, self.ldf_.values.shape[2], 2) attachment_age = self.attachment_age if self.attachment_age else X.ddims[-2] - self.ldf_.values = xp.concatenate(( - self.ldf_.values[..., : sum(self.ldf_.ddims < attachment_age)], - fitted[..., -sum(self.ldf_.ddims >= attachment_age) :],), - axis=-1,) + self.ldf_.values = xp.concatenate( + ( + self.ldf_.values[..., : sum(self.ldf_.ddims < attachment_age)], + fitted[..., -sum(self.ldf_.ddims >= attachment_age) :], + ), + axis=-1, + ) self.omega_ = model.omega_ self.theta_ = model.theta_ self.G_ = model.G_ @@ -169,7 +177,9 @@ def fit(self, X, y=None, sample_weight=None): self.elr_ = model.elr_ self.norm_resid_ = model.norm_resid_ if self.truncation_age: - self.ldf_.values[..., -1:] = self.ldf_.values[..., -1:] * self.G_(self.truncation_age).values + self.ldf_.values[..., -1:] = ( + self.ldf_.values[..., -1:] * self.G_(self.truncation_age).values + ) # self._get_tail_stats(self) if backend == "cupy": self = self.set_backend("cupy", inplace=True) diff --git a/chainladder/tails/tests/test_bondy.py b/chainladder/tails/tests/test_bondy.py index 79d2deccc..886c7c32b 100644 --- a/chainladder/tails/tests/test_bondy.py +++ b/chainladder/tails/tests/test_bondy.py @@ -4,4 +4,7 @@ def test_bondy1(): tri = cl.load_sample("tail_sample")["paid"] dev = cl.Development(average="simple").fit_transform(tri) - assert round(float(cl.TailBondy(earliest_age=12).fit(dev).cdf_.values[0, 0, 0, -2]), 3) == 1.028 + assert ( + round(float(cl.TailBondy(earliest_age=12).fit(dev).cdf_.values[0, 0, 0, -2]), 3) + == 1.028 + ) diff --git a/chainladder/tails/tests/test_constant.py b/chainladder/tails/tests/test_constant.py index b742ca0fd..aa34e4583 100644 --- a/chainladder/tails/tests/test_constant.py +++ b/chainladder/tails/tests/test_constant.py @@ -1,12 +1,14 @@ import chainladder as cl + def test_constant_balances(qtr): xp = qtr.get_array_module() assert ( round( float( xp.prod( - cl.TailConstant(1.05, decay=0.8) + cl + .TailConstant(1.05, decay=0.8) .fit(qtr) .ldf_.iloc[0, 1] .values[0, 0, 0, -5:] diff --git a/chainladder/tails/tests/test_exponential.py b/chainladder/tails/tests/test_exponential.py index ccfc70b94..5940f445e 100644 --- a/chainladder/tails/tests/test_exponential.py +++ b/chainladder/tails/tests/test_exponential.py @@ -7,7 +7,8 @@ def test_fit_period(): dev = cl.Development(average="simple").fit_transform(tri) assert ( round( - cl.TailCurve(fit_period=(tri.ddims[-7], None), extrap_periods=10) + cl + .TailCurve(fit_period=(tri.ddims[-7], None), extrap_periods=10) .fit(dev) .cdf_["paid"] .set_backend("numpy", inplace=True) @@ -24,10 +25,8 @@ def test_curve_validation(): """ with pytest.raises(ValueError): - tri = cl.load_sample('tail_sample') - cl.TailCurve( - curve='Exponential' - ).fit_transform(tri) + tri = cl.load_sample("tail_sample") + cl.TailCurve(curve="Exponential").fit_transform(tri) def test_errors_validation(): @@ -35,7 +34,5 @@ def test_errors_validation(): Test validation of the errors parameter. Should raise a value error if an incorrect argument is supplied. """ with pytest.raises(ValueError): - tri = cl.load_sample('tail_sample') - cl.TailCurve( - errors='Ignore' - ).fit_transform(tri) + tri = cl.load_sample("tail_sample") + cl.TailCurve(errors="Ignore").fit_transform(tri) diff --git a/chainladder/utils/__init__.py b/chainladder/utils/__init__.py index 47705be62..02baea569 100644 --- a/chainladder/utils/__init__.py +++ b/chainladder/utils/__init__.py @@ -1,6 +1,7 @@ -""" utils should store all utility functions and classes, i.e. things that - are used by various modules in the package. +"""utils should store all utility functions and classes, i.e. things that +are used by various modules in the package. """ + from chainladder.utils.weighted_regression import ( WeightedRegression, ) # noqa (API import) @@ -20,7 +21,7 @@ minimum, maximum, PatsyFormula, - model_diagnostics + model_diagnostics, ) from chainladder.utils.cupy import cp from chainladder.utils.sparse import sp diff --git a/chainladder/workflow/__init__.py b/chainladder/workflow/__init__.py index fe6f35bfa..f76e52b7e 100644 --- a/chainladder/workflow/__init__.py +++ b/chainladder/workflow/__init__.py @@ -2,9 +2,4 @@ from chainladder.workflow.voting import VotingChainladder # noqa (API import) from chainladder.workflow.voting import TriangleSelector # noqa (API import) -__all__ = [ - "GridSearch", - "Pipeline", - "VotingChainladder", - "TriangleSelector" -] +__all__ = ["GridSearch", "Pipeline", "VotingChainladder", "TriangleSelector"] diff --git a/chainladder/workflow/gridsearch.py b/chainladder/workflow/gridsearch.py index cd55c7f18..269cf67f1 100644 --- a/chainladder/workflow/gridsearch.py +++ b/chainladder/workflow/gridsearch.py @@ -99,8 +99,15 @@ def ldf_by_age(model): """ - def __init__(self, estimator, param_grid, scoring, verbose=0, - error_score="raise", n_jobs=None): + def __init__( + self, + estimator, + param_grid, + scoring, + verbose=0, + error_score="raise", + n_jobs=None, + ): self.estimator = estimator self.param_grid = param_grid self.scoring = scoring @@ -136,16 +143,17 @@ def _fit_single_estimator(estimator, fit_params, X, y, scoring, item): for score in scoring.keys(): item[score] = scoring[score](model) return item - - results_ = Parallel(n_jobs=self.n_jobs)(delayed(_fit_single_estimator)( - self.estimator, fit_params, X, y, scoring, item) - for item in grid) + + results_ = Parallel(n_jobs=self.n_jobs)( + delayed(_fit_single_estimator)( + self.estimator, fit_params, X, y, scoring, item + ) + for item in grid + ) self.results_ = pd.DataFrame(results_) return self - - class Pipeline(PipelineSL, EstimatorIO): """This is a near direct of copy the scikit-learn Pipeline class. @@ -258,13 +266,11 @@ def fit_predict(self, X, y=None, sample_weight=None, **fit_params): return self.predict(X, sample_weight, **fit_params) def to_json(self): - return json.dumps( - [ - { - "name": item[0], - "params": item[1].get_params(), - "__class__": item[1].__class__.__name__, - } - for item in self.steps - ] - ) + return json.dumps([ + { + "name": item[0], + "params": item[1].get_params(), + "__class__": item[1].__class__.__name__, + } + for item in self.steps + ]) diff --git a/chainladder/workflow/tests/test_predict.py b/chainladder/workflow/tests/test_predict.py index d4c61095c..e6aee6941 100644 --- a/chainladder/workflow/tests/test_predict.py +++ b/chainladder/workflow/tests/test_predict.py @@ -16,14 +16,11 @@ def test_voting_predict(): bf = cl.BornhuetterFerguson() cc = cl.CapeCod() - estimators = [('bcl', bcl), ('bf', bf), ('cc', cc)] + estimators = [("bcl", bcl), ("bf", bf), ("cc", cc)] weights = np.array([[1, 2, 3]] * 3 + [[0, 0.5, 0.5]] * 3 + [[0, 0, 1]] * 3) - vot = cl.VotingChainladder( - estimators=estimators, - weights=weights - ).fit( - raa_1989, - sample_weight=apriori_1989, - ) + vot = cl.VotingChainladder(estimators=estimators, weights=weights).fit( + raa_1989, + sample_weight=apriori_1989, + ) vot.predict(raa_1990, sample_weight=apriori_1990) diff --git a/docs/_ext/tests/test_ext_linkcode.py b/docs/_ext/tests/test_ext_linkcode.py index 1e94afd13..7fb7e5e24 100644 --- a/docs/_ext/tests/test_ext_linkcode.py +++ b/docs/_ext/tests/test_ext_linkcode.py @@ -1,5 +1,6 @@ from linkcode import linkcode_resolve + def test_linkcode_resolve_url_lines() -> None: """ Tests the linkcode_resolve function. Ideally we want: @@ -8,19 +9,15 @@ def test_linkcode_resolve_url_lines() -> None: - Starting and ending line numbers in the URL. """ url: str = linkcode_resolve( - domain='py', - info={ - 'module': 'chainladder', - 'fullname': 'Benktander' - } + domain="py", info={"module": "chainladder", "fullname": "Benktander"} ) assert url is not None - assert url.startswith('https://github.com/casact/chainladder-python/blob/master/') + assert url.startswith("https://github.com/casact/chainladder-python/blob/master/") # Extract the line numbers. - lines: str = url.split('#')[1] - start, end = [int(x[1:]) for x in lines.split('-')] + lines: str = url.split("#")[1] + start, end = [int(x[1:]) for x in lines.split("-")] # Check the line numbers. assert start > 0 diff --git a/docs/conf.py b/docs/conf.py index addaeed64..529340753 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -1,6 +1,7 @@ import sys from pathlib import Path -_DOCS_EXT_DIR = Path(__file__).resolve().parent / '_ext' + +_DOCS_EXT_DIR = Path(__file__).resolve().parent / "_ext" _p = str(_DOCS_EXT_DIR) if _p not in sys.path: sys.path.insert(0, _p) @@ -10,38 +11,89 @@ # If you wish to continue using _config.yml, make edits to that file and # re-generate this one. ############################################################################### -author = 'John Bogaardt, Kenneth Hsu, et. al.' +author = "John Bogaardt, Kenneth Hsu, et. al." autosummary_generate = True -bibtex_bibfiles = ['library/references.bib'] -bibtex_reference_style = 'author_year' -comments_config = {'hypothesis': False, 'utterances': False} -copyright = '2023' -exclude_patterns = ['**.ipynb_checkpoints', '.DS_Store', 'Thumbs.db', '_build'] -extensions = ['sphinx_togglebutton', 'sphinx_copybutton', 'myst_nb', 'jupyter_book', 'sphinx_thebe', 'sphinx_comments', 'sphinx_external_toc', 'sphinx.ext.intersphinx', 'sphinx_design', 'sphinx_book_theme', 'sphinx.ext.autodoc', 'sphinx.ext.autosummary', 'sphinx.ext.doctest', 'sphinx.ext.linkcode', 'numpydoc', 'sphinx.ext.mathjax', 'linkcode', 'sphinxcontrib.bibtex', 'sphinx_jupyterbook_latex', 'sphinx_multitoc_numbering'] +bibtex_bibfiles = ["library/references.bib"] +bibtex_reference_style = "author_year" +comments_config = {"hypothesis": False, "utterances": False} +copyright = "2023" +exclude_patterns = ["**.ipynb_checkpoints", ".DS_Store", "Thumbs.db", "_build"] +extensions = [ + "sphinx_togglebutton", + "sphinx_copybutton", + "myst_nb", + "jupyter_book", + "sphinx_thebe", + "sphinx_comments", + "sphinx_external_toc", + "sphinx.ext.intersphinx", + "sphinx_design", + "sphinx_book_theme", + "sphinx.ext.autodoc", + "sphinx.ext.autosummary", + "sphinx.ext.doctest", + "sphinx.ext.linkcode", + "numpydoc", + "sphinx.ext.mathjax", + "linkcode", + "sphinxcontrib.bibtex", + "sphinx_jupyterbook_latex", + "sphinx_multitoc_numbering", +] external_toc_exclude_missing = False -external_toc_path = '_toc.yml' -html_baseurl = '' -html_favicon = '' -html_logo = 'images/logo.png' -html_sourcelink_suffix = '' -html_theme = 'sphinx_book_theme' -html_theme_options = {'search_bar_text': 'Search this book...', 'launch_buttons': {'notebook_interface': 'classic', 'binderhub_url': '', 'jupyterhub_url': '', 'thebe': False, 'colab_url': '', 'deepnote_url': ''}, 'path_to_docs': 'docs', 'repository_url': 'https://github.com/casact/chainladder-python', 'repository_branch': 'master', 'extra_footer': '', 'home_page_in_toc': True, 'announcement': '', 'analytics': {'google_analytics_id': '', 'plausible_analytics_domain': '', 'plausible_analytics_url': 'https://plausible.io/js/script.js'}, 'use_repository_button': True, 'use_edit_page_button': False, 'use_issues_button': True} -html_title = 'Chainladder - Python' -intersphinx_mapping = {'sklearn': ['https://scikit-learn.org/stable/', None]} -latex_engine = 'pdflatex' -myst_enable_extensions = ['html_admonition', 'colon_fence', 'dollarmath', 'linkify', 'substitution'] -myst_url_schemes = ['mailto', 'http', 'https'] +external_toc_path = "_toc.yml" +html_baseurl = "" +html_favicon = "" +html_logo = "images/logo.png" +html_sourcelink_suffix = "" +html_theme = "sphinx_book_theme" +html_theme_options = { + "search_bar_text": "Search this book...", + "launch_buttons": { + "notebook_interface": "classic", + "binderhub_url": "", + "jupyterhub_url": "", + "thebe": False, + "colab_url": "", + "deepnote_url": "", + }, + "path_to_docs": "docs", + "repository_url": "https://github.com/casact/chainladder-python", + "repository_branch": "master", + "extra_footer": "", + "home_page_in_toc": True, + "announcement": "", + "analytics": { + "google_analytics_id": "", + "plausible_analytics_domain": "", + "plausible_analytics_url": "https://plausible.io/js/script.js", + }, + "use_repository_button": True, + "use_edit_page_button": False, + "use_issues_button": True, +} +html_title = "Chainladder - Python" +intersphinx_mapping = {"sklearn": ["https://scikit-learn.org/stable/", None]} +latex_engine = "pdflatex" +myst_enable_extensions = [ + "html_admonition", + "colon_fence", + "dollarmath", + "linkify", + "substitution", +] +myst_url_schemes = ["mailto", "http", "https"] nb_execution_allow_errors = False -nb_execution_cache_path = '' +nb_execution_cache_path = "" nb_execution_excludepatterns = [] nb_execution_in_temp = False -nb_execution_mode = 'force' +nb_execution_mode = "force" nb_execution_timeout = 30 -nb_output_stderr = 'show' +nb_output_stderr = "show" numfig = True numpydoc_show_class_members = False -pygments_style = 'sphinx' -suppress_warnings = ['myst.domains'] -templates_path = ['_templates'] +pygments_style = "sphinx" +suppress_warnings = ["myst.domains"] +templates_path = ["_templates"] use_jupyterbook_latex = True use_multitoc_numbering = True diff --git a/docs/friedland/chapter_10.ipynb b/docs/friedland/chapter_10.ipynb index 33f849973..078e7495e 100644 --- a/docs/friedland/chapter_10.ipynb +++ b/docs/friedland/chapter_10.ipynb @@ -411,7 +411,8 @@ "# Chapter 7 selection: three-year simple average reported development, 1.000 tail.\n", "# Friedland cumulates the age-to-age factors rounded to three decimals.\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_reported_dev.ldf_ = ia_reported_dev.ldf_.round(3)\n", "\n", "# CapeCod derives the expected claim ratio (apriori) from the reported claims and\n", @@ -1140,8 +1141,11 @@ "# Exhibit I, Sheet 1 - all-years estimated claim ratio\n", "assert np.isclose(ia_apriori, 0.695, atol=1e-3)\n", "# Exhibit I, Sheet 1 - selected CDFs to ultimate\n", - "assert np.allclose(ia_cdf.round(3),\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(\n", + " ia_cdf.round(3),\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", "# Exhibit I, Sheet 2 - projected ultimate claims\n", "assert np.isclose(ia_ult.sum(), 570800677, rtol=5e-3)\n", "# Exhibit I, Sheet 3 - estimated IBNR\n", @@ -1654,8 +1658,18 @@ "# method (vertical_line=True) reproduces its on-level and tort factors, and the\n", "# 3.42% pure-premium trend is applied through CapeCod's trend parameter.\n", "rate_history = pd.DataFrame({\n", - " \"date\": [\"1/1/1999\", \"1/1/2000\", \"1/1/2001\", \"1/1/2002\", \"1/1/2003\",\n", - " \"1/1/2004\", \"1/1/2005\", \"1/1/2006\", \"1/1/2007\", \"1/1/2008\"],\n", + " \"date\": [\n", + " \"1/1/1999\",\n", + " \"1/1/2000\",\n", + " \"1/1/2001\",\n", + " \"1/1/2002\",\n", + " \"1/1/2003\",\n", + " \"1/1/2004\",\n", + " \"1/1/2005\",\n", + " \"1/1/2006\",\n", + " \"1/1/2007\",\n", + " \"1/1/2008\",\n", + " ],\n", " \"rate_change\": [0.02, 0.02, 0.02, 0.02, 0.05, 0.075, 0.15, 0.10, -0.20, -0.20],\n", "})\n", "tort_history = pd.DataFrame({\n", @@ -1665,16 +1679,19 @@ "pp_trend_rate = 0.0342\n", "\n", "onlevel = cl.ParallelogramOLF(\n", - " rate_history, change_col=\"rate_change\", date_col=\"date\", vertical_line=True)\n", + " rate_history, change_col=\"rate_change\", date_col=\"date\", vertical_line=True\n", + ")\n", "tort = cl.ParallelogramOLF(\n", - " tort_history, change_col=\"rate_change\", date_col=\"date\", vertical_line=True)\n", + " tort_history, change_col=\"rate_change\", date_col=\"date\", vertical_line=True\n", + ")\n", "onlevel_adj = col(onlevel.fit(xyz_premium).olf_)\n", "tort_reform = col(tort.fit(xyz_reported.latest_diagonal).olf_)\n", "pp_trend = (1 + pp_trend_rate) ** (max(xyz_years) - np.array(xyz_years))\n", "\n", "# Reported cumulative development factors are floored at 1.0 (caps %reported at 100%).\n", "xyz_cdf = np.maximum(\n", - " xyz_reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten()[::-1], 1.0).round(3)\n", + " xyz_reported_dev.cdf_.to_frame(origin_as_datetime=False).values.flatten()[::-1], 1.0\n", + ").round(3)\n", "xyz_pct_reported = 1 / xyz_cdf\n", "xyz_reported_latest = col(xyz_reported.latest_diagonal)\n", "xyz_prem = col(xyz_premium)\n", @@ -1686,15 +1703,23 @@ "# Chain the adjustments into a pipeline to derive the all-years apriori: the\n", "# tort-reform OLF and the floored Chapter 7 pattern develop the reported claims,\n", "# CapeCod applies the pure-premium trend, and on-level premium is the exposure.\n", - "floored_patterns = dict(zip(\n", - " [int(age) for age in xyz_reported_dev.cdf_.ddims],\n", - " np.maximum(xyz_reported_dev.cdf_.values.flatten(), 1.0)))\n", + "floored_patterns = dict(\n", + " zip(\n", + " [int(age) for age in xyz_reported_dev.cdf_.ddims],\n", + " np.maximum(xyz_reported_dev.cdf_.values.flatten(), 1.0),\n", + " )\n", + ")\n", "onlevel_premium = xyz_premium * onlevel.olf_\n", - "xyz_cc = cl.Pipeline([\n", - " (\"tort\", tort),\n", - " (\"dev\", cl.DevelopmentConstant(patterns=floored_patterns, style=\"cdf\")),\n", - " (\"capecod\", cl.CapeCod(trend=pp_trend_rate)),\n", - "]).fit(xyz_reported, sample_weight=onlevel_premium).named_steps[\"capecod\"]\n", + "xyz_cc = (\n", + " cl\n", + " .Pipeline([\n", + " (\"tort\", tort),\n", + " (\"dev\", cl.DevelopmentConstant(patterns=floored_patterns, style=\"cdf\")),\n", + " (\"capecod\", cl.CapeCod(trend=pp_trend_rate)),\n", + " ])\n", + " .fit(xyz_reported, sample_weight=onlevel_premium)\n", + " .named_steps[\"capecod\"]\n", + ")\n", "xyz_apriori = float(xyz_cc.apriori_.to_frame().iloc[0, 0])\n", "\n", "sheet1 = pd.DataFrame(index=xyz_years)\n", @@ -2454,11 +2479,17 @@ "# Exhibit II, Sheet 1 - all-years adjusted claim ratio\n", "assert np.isclose(xyz_apriori, 0.708, atol=2e-3)\n", "# Exhibit II, Sheet 1 - selected CDFs to ultimate (limited to a minimum of 1.00)\n", - "assert np.allclose(xyz_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(\n", + " xyz_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", "# Exhibit II, Sheet 1 - detrended (unadjusted) claim ratios, Column 15\n", - "assert np.allclose(xyz_unadjusted_ratio,\n", - " [0.746, 0.757, 0.767, 0.778, 0.789, 0.777, 0.747, 0.672, 0.565, 0.547, 0.708], atol=2e-3)\n", + "assert np.allclose(\n", + " xyz_unadjusted_ratio,\n", + " [0.746, 0.757, 0.767, 0.778, 0.789, 0.777, 0.747, 0.672, 0.565, 0.547, 0.708],\n", + " atol=2e-3,\n", + ")\n", "# Exhibit II, Sheet 2 - projected ultimate claims\n", "assert np.isclose(xyz_ult.sum(), 504300, rtol=5e-3)\n", "# Exhibit II, Sheet 3 - estimated IBNR\n", @@ -3170,22 +3201,130 @@ "summary_ult = pd.DataFrame(index=xyz_years)\n", "summary_ult[\"Reported\"] = xyz_reported_latest\n", "summary_ult[\"Paid\"] = xyz_paid_latest\n", - "summary_ult[\"Development (Reported)\"] = [15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530]\n", - "summary_ult[\"Development (Paid)\"] = [15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995]\n", - "summary_ult[\"Expected Claims\"] = [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433]\n", - "summary_ult[\"BF (Reported)\"] = [15822, 25107, 37246, 38798, 48312, 45068, 75492, 79129, 60404, 45221, 42607]\n", - "summary_ult[\"BF (Paid)\"] = [15977, 25158, 37841, 40525, 49417, 50768, 82593, 94301, 71205, 45636, 41049]\n", + "summary_ult[\"Development (Reported)\"] = [\n", + " 15822,\n", + " 25082,\n", + " 36948,\n", + " 38487,\n", + " 48313,\n", + " 44950,\n", + " 74787,\n", + " 76661,\n", + " 58370,\n", + " 47979,\n", + " 47530,\n", + "]\n", + "summary_ult[\"Development (Paid)\"] = [\n", + " 15980,\n", + " 25164,\n", + " 37922,\n", + " 40600,\n", + " 49592,\n", + " 49858,\n", + " 80537,\n", + " 80333,\n", + " 72108,\n", + " 77941,\n", + " 74995,\n", + "]\n", + "summary_ult[\"Expected Claims\"] = [\n", + " 15660,\n", + " 24665,\n", + " 35235,\n", + " 39150,\n", + " 47906,\n", + " 54164,\n", + " 86509,\n", + " 108172,\n", + " 70786,\n", + " 39835,\n", + " 39433,\n", + "]\n", + "summary_ult[\"BF (Reported)\"] = [\n", + " 15822,\n", + " 25107,\n", + " 37246,\n", + " 38798,\n", + " 48312,\n", + " 45068,\n", + " 75492,\n", + " 79129,\n", + " 60404,\n", + " 45221,\n", + " 42607,\n", + "]\n", + "summary_ult[\"BF (Paid)\"] = [\n", + " 15977,\n", + " 25158,\n", + " 37841,\n", + " 40525,\n", + " 49417,\n", + " 50768,\n", + " 82593,\n", + " 94301,\n", + " 71205,\n", + " 45636,\n", + " 41049,\n", + "]\n", "summary_ult[\"Cape Cod\"] = xyz_ult.round(0)\n", "display(summary_ult)\n", "\n", "# Estimated IBNR by method (Sheet 5): projected ultimate minus reported claims.\n", "summary_ibnr = pd.DataFrame(index=xyz_years)\n", "summary_ibnr[\"Case Outstanding\"] = (xyz_reported_latest - xyz_paid_latest).round(0)\n", - "summary_ibnr[\"Development (Reported)\"] = [0, -25, -298, -310, 145, 577, 4498, 6006, 9566, 16247, 28898]\n", - "summary_ibnr[\"Development (Paid)\"] = [158, 58, 676, 1802, 1423, 5485, 10249, 9678, 23304, 46209, 56363]\n", - "summary_ibnr[\"Expected Claims\"] = [-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801]\n", + "summary_ibnr[\"Development (Reported)\"] = [\n", + " 0,\n", + " -25,\n", + " -298,\n", + " -310,\n", + " 145,\n", + " 577,\n", + " 4498,\n", + " 6006,\n", + " 9566,\n", + " 16247,\n", + " 28898,\n", + "]\n", + "summary_ibnr[\"Development (Paid)\"] = [\n", + " 158,\n", + " 58,\n", + " 676,\n", + " 1802,\n", + " 1423,\n", + " 5485,\n", + " 10249,\n", + " 9678,\n", + " 23304,\n", + " 46209,\n", + " 56363,\n", + "]\n", + "summary_ibnr[\"Expected Claims\"] = [\n", + " -162,\n", + " -442,\n", + " -2011,\n", + " 352,\n", + " -262,\n", + " 9791,\n", + " 16221,\n", + " 37517,\n", + " 21982,\n", + " 8103,\n", + " 20801,\n", + "]\n", "summary_ibnr[\"BF (Reported)\"] = [0, 0, 0, 0, 143, 695, 5204, 8474, 11600, 13489, 23975]\n", - "summary_ibnr[\"BF (Paid)\"] = [155, 51, 595, 1728, 1248, 6396, 12305, 23646, 22401, 13904, 22417]\n", + "summary_ibnr[\"BF (Paid)\"] = [\n", + " 155,\n", + " 51,\n", + " 595,\n", + " 1728,\n", + " 1248,\n", + " 6396,\n", + " 12305,\n", + " 23646,\n", + " 22401,\n", + " 13904,\n", + " 22417,\n", + "]\n", "summary_ibnr[\"Cape Cod\"] = (xyz_ult - xyz_reported_latest).round(0)\n", "display(summary_ibnr)\n", "\n", @@ -4695,7 +4834,8 @@ " # Chapter 7 selection: five-year simple average reported development, 1.000\n", " # tail; CDFs cumulated from age-to-age factors rounded to three decimals.\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", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", " reported_dev.ldf_ = reported_dev.ldf_.round(3)\n", "\n", " cc = cl.CapeCod().fit(reported_dev, sample_weight=premium)\n", @@ -4760,7 +4900,11 @@ "assert np.isclose(pp_apriori[\"Steady-State\"], 0.700, atol=2e-3)\n", "assert np.isclose(pp_apriori[\"Increasing Claim Ratios\"], 0.807, atol=2e-3)\n", "assert np.isclose(pp_apriori[\"Increasing Case Outstanding Strength\"], 0.717, atol=2e-3)\n", - "assert np.isclose(pp_apriori[\"Increasing Claim Ratios and Case Outstanding Strength\"], 0.831, atol=2e-3)\n", + "assert np.isclose(\n", + " pp_apriori[\"Increasing Claim Ratios and Case Outstanding Strength\"],\n", + " 0.831,\n", + " atol=2e-3,\n", + ")\n", "\n", "# Estimated IBNR reconciles for the two scenarios with clean sample data.\n", "assert np.isclose(pp_ibnr[\"Steady-State\"], 438638, rtol=5e-3)\n", @@ -5542,7 +5686,8 @@ "\n", " # Chapter 7 selection: five-year simple average reported development, 1.000 tail.\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", + " cl.Development(n_periods=5, average=\"simple\").fit_transform(reported)\n", + " )\n", " reported_dev.ldf_ = reported_dev.ldf_.round(3)\n", "\n", " cc = cl.CapeCod().fit(reported_dev, sample_weight=premium)\n", @@ -5571,7 +5716,8 @@ "\n", "\n", "us_auto_results = {\n", - " label: us_auto_cc_scenario(scenario) for label, scenario in us_auto_scenarios.items()\n", + " label: us_auto_cc_scenario(scenario)\n", + " for label, scenario in us_auto_scenarios.items()\n", "}\n", "for name, (apriori, table) in us_auto_results.items():\n", " print(f\"{name} - all-years estimated claim ratio {apriori:.3f}\")\n", @@ -5598,15 +5744,21 @@ "outputs": [], "source": [ "us_apriori = {name: apriori for name, (apriori, table) in us_auto_results.items()}\n", - "us_ibnr = {name: table[\"IBNR\"].sum() for name, (apriori, table) in us_auto_results.items()}\n", + "us_ibnr = {\n", + " name: table[\"IBNR\"].sum() for name, (apriori, table) in us_auto_results.items()\n", + "}\n", "\n", "# Both scenarios derive the text's 75.0% all-years estimated claim ratio.\n", - "assert np.isclose(us_apriori[\"Steady-State (No Change in Product Mix)\"], 0.750, atol=2e-3)\n", + "assert np.isclose(\n", + " us_apriori[\"Steady-State (No Change in Product Mix)\"], 0.750, atol=2e-3\n", + ")\n", "assert np.isclose(us_apriori[\"Changing Product Mix\"], 0.750, atol=2e-3)\n", "\n", "# Steady-state reconciles to the actual IBNR requirement; the changing product mix\n", "# understates it (the text's actual is 2,391,084).\n", - "assert np.isclose(us_ibnr[\"Steady-State (No Change in Product Mix)\"], 1394634, rtol=6e-3)\n", + "assert np.isclose(\n", + " us_ibnr[\"Steady-State (No Change in Product Mix)\"], 1394634, rtol=6e-3\n", + ")\n", "assert np.isclose(us_ibnr[\"Changing Product Mix\"], 2168000, rtol=6e-3)" ] } diff --git a/docs/friedland/chapter_7_part_2.ipynb b/docs/friedland/chapter_7_part_2.ipynb index 442773087..7814be635 100644 --- a/docs/friedland/chapter_7_part_2.ipynb +++ b/docs/friedland/chapter_7_part_2.ipynb @@ -46,7 +46,7 @@ " \"ignore\",\n", " message=r\"Some exclusions have been ignored\\..*link ratio\\(s\\) is required.*\",\n", " category=UserWarning,\n", - ")\n" + ")" ] }, { @@ -128,46 +128,64 @@ }, "outputs": [], "source": [ - "def dev_exhibit(tri: cl.Triangle, avg_params: dict[str, int], selected_avg: str, tail: float) -> dict[cl.Triangle()]:\n", - " display('')\n", - " display(HTML(\"\"\"\n", + "def dev_exhibit(\n", + " tri: cl.Triangle, avg_params: dict[str, int], selected_avg: str, tail: float\n", + ") -> dict[cl.Triangle()]:\n", + " display(\"\")\n", + " display(\n", + " HTML(\"\"\"\n", "

\n", " PART 1 - Data Triangle\n", "

\n", - " \"\"\"))\n", + " \"\"\")\n", + " )\n", " display(tri)\n", - " display(HTML(\"\"\"\n", + " display(\n", + " HTML(\"\"\"\n", "

\n", " PART 2 - Age-to-Age Factors\n", "

\n", - " \"\"\"))\n", - " age_to_age_df = tri.age_to_age.to_frame(origin_as_datetime=False)\n", - " display(\n", - " age_to_age_df.style.format(precision=3, na_rep=\"\")\n", + " \"\"\")\n", " )\n", + " age_to_age_df = tri.age_to_age.to_frame(origin_as_datetime=False)\n", + " display(age_to_age_df.style.format(precision=3, na_rep=\"\"))\n", " devs = {}\n", - " display(HTML(\"\"\"\n", + " display(\n", + " HTML(\"\"\"\n", "

\n", " PART 3 - Average Age-to-Age Factor\n", "

\n", - " \"\"\"))\n", + " \"\"\")\n", + " )\n", " for k, v in avg_params.items():\n", " devs[k] = cl.Development(**v).fit_transform(tri)\n", + "\n", " def print_ldfs(ldf_dict: dict[cl.Triangle()]):\n", " with pd.option_context(\"display.float_format\", \"{:.3f}\".format):\n", - " display(pd.concat([v.to_frame().rename(index={'(All)': k}) for k, v in ldf_dict.items()]))\n", + " display(\n", + " pd.concat([\n", + " v.to_frame().rename(index={\"(All)\": k}) for k, v in ldf_dict.items()\n", + " ])\n", + " )\n", " return None\n", + "\n", " print_ldfs({k: v.ldf_.round(decimals=3) for k, v in devs.items()})\n", - " devs[\"Selected\"] = cl.TailConstant(tail=tail, projection_period=0).fit_transform(devs[selected_avg])\n", + " devs[\"Selected\"] = cl.TailConstant(tail=tail, projection_period=0).fit_transform(\n", + " devs[selected_avg]\n", + " )\n", " selected = {}\n", - " selected['CDF to Ultimate'] = devs[\"Selected\"].ldf_.round(decimals=3).incr_to_cum().round(decimals=3)\n", - " selected['Percent Reported'] = (1 / selected['CDF to Ultimate']).round(decimals=3)\n", - " display(HTML(\"\"\"\n", + " selected[\"CDF to Ultimate\"] = (\n", + " devs[\"Selected\"].ldf_.round(decimals=3).incr_to_cum().round(decimals=3)\n", + " )\n", + " selected[\"Percent Reported\"] = (1 / selected[\"CDF to Ultimate\"]).round(decimals=3)\n", + " display(\n", + " HTML(\"\"\"\n", "

\n", " PART 4 - Selected Age-to-Age Factor\n", "

\n", - " \"\"\"))\n", - " print_ldfs({'Selected': devs['Selected'].ldf_.round(decimals=3)})\n", + " \"\"\")\n", + " )\n", + " print_ldfs({\"Selected\": devs[\"Selected\"].ldf_.round(decimals=3)})\n", " print_ldfs(selected)\n", " return devs" ] @@ -203,16 +221,12 @@ }, "outputs": [], "source": [ - "def ex3sht1(\n", - " tri: cl.Triangle,\n", - " dev_input: dict,\n", - " tail_input: dict\n", - ") -> tuple:\n", - " \n", + "def ex3sht1(tri: cl.Triangle, dev_input: dict, tail_input: dict) -> tuple:\n", + "\n", " def format_col(x, reverse=False):\n", - " if reverse is True:\n", - " return x.to_frame().squeeze()[::-1].reset_index(drop=True)\n", - " return x.to_frame().squeeze().reset_index(drop=True)\n", + " if reverse is True:\n", + " return x.to_frame().squeeze()[::-1].reset_index(drop=True)\n", + " return x.to_frame().squeeze().reset_index(drop=True)\n", "\n", " def col_diff(x, y):\n", " return x.squeeze() - y.squeeze()\n", @@ -230,24 +244,56 @@ " ult = model.ultimate_\n", "\n", " col1 = pd.DataFrame({\"Accident Year\": format_col(tri.origin.astype(str))})\n", - " col2 = pd.DataFrame({\"Earned Premium\": format_col(tri.latest_diagonal.loc[\"Steady State\", \"Earned Premium\"])})\n", - " col4 = pd.DataFrame({\"Ult. Claims\": format_col(ult.loc[\"Steady State\", \"Reported Claims\"])})\n", + " col2 = pd.DataFrame({\n", + " \"Earned Premium\": format_col(\n", + " tri.latest_diagonal.loc[\"Steady State\", \"Earned Premium\"]\n", + " )\n", + " })\n", + " col4 = pd.DataFrame({\n", + " \"Ult. Claims\": format_col(ult.loc[\"Steady State\", \"Reported Claims\"])\n", + " })\n", " col3 = pd.DataFrame({\"Ult. Claim Ratio\": col_div(col4, col2)})\n", - " col5 = pd.DataFrame({\"Rep. Claims 12/31/08\": format_col(tri.latest_diagonal.loc[\"Steady State\", \"Reported Claims\"])})\n", + " col5 = pd.DataFrame({\n", + " \"Rep. Claims 12/31/08\": format_col(\n", + " tri.latest_diagonal.loc[\"Steady State\", \"Reported Claims\"]\n", + " )\n", + " })\n", " col6 = pd.DataFrame({\"Actual IBNR\": col_diff(col4, col5)})\n", - " col8 = pd.DataFrame({\"Ult. Claims\": format_col(ult.loc[\"Increasing Claim\", \"Reported Claims\"])})\n", - " col9 = pd.DataFrame({\"Rep. Claims 12/31/08\": format_col(tri.latest_diagonal.loc[\"Increasing Claim\", \"Reported Claims\"])})\n", + " col8 = pd.DataFrame({\n", + " \"Ult. Claims\": format_col(ult.loc[\"Increasing Claim\", \"Reported Claims\"])\n", + " })\n", + " col9 = pd.DataFrame({\n", + " \"Rep. Claims 12/31/08\": format_col(\n", + " tri.latest_diagonal.loc[\"Increasing Claim\", \"Reported Claims\"]\n", + " )\n", + " })\n", " col7 = pd.DataFrame({\"Ult. Claim Ratio\": col_div(col8, col2)})\n", " col10 = pd.DataFrame({\"Actual IBNR\": col_diff(col8, col9)})\n", " col11 = pd.DataFrame({\"Accident Year\": format_col(tri.origin.astype(str))})\n", - " col12 = pd.DataFrame({\"Earned Premium\": format_col(tri.latest_diagonal.loc[\"Steady State\", \"Earned Premium\"])})\n", - " col14 = pd.DataFrame({\"Ult. Claims\": format_col(ult.loc[\"Steady State\", \"Reported Claims\"])})\n", + " col12 = pd.DataFrame({\n", + " \"Earned Premium\": format_col(\n", + " tri.latest_diagonal.loc[\"Steady State\", \"Earned Premium\"]\n", + " )\n", + " })\n", + " col14 = pd.DataFrame({\n", + " \"Ult. Claims\": format_col(ult.loc[\"Steady State\", \"Reported Claims\"])\n", + " })\n", " col13 = pd.DataFrame({\"Ult. Claim Ratio\": col_div(col14, col12)})\n", - " col15 = pd.DataFrame({\"Rep. Claims 12/31/08\": format_col(tri.latest_diagonal.loc[\"Increasing Case\", \"Reported Claims\"])})\n", + " col15 = pd.DataFrame({\n", + " \"Rep. Claims 12/31/08\": format_col(\n", + " tri.latest_diagonal.loc[\"Increasing Case\", \"Reported Claims\"]\n", + " )\n", + " })\n", " col16 = pd.DataFrame({\"Actual IBNR\": col_diff(col14, col15)})\n", - " col18 = pd.DataFrame({\"Ult. Claims\": format_col(ult.loc[\"Increasing Claim\", \"Reported Claims\"])})\n", + " col18 = pd.DataFrame({\n", + " \"Ult. Claims\": format_col(ult.loc[\"Increasing Claim\", \"Reported Claims\"])\n", + " })\n", " col17 = pd.DataFrame({\"Ult. Claim Ratio\": col_div(col18, col12)})\n", - " col19 = pd.DataFrame({\"Rep. Claims 12/31/08\": format_col(tri.latest_diagonal.loc[\"Increasing Claim Case\", \"Reported Claims\"])})\n", + " col19 = pd.DataFrame({\n", + " \"Rep. Claims 12/31/08\": format_col(\n", + " tri.latest_diagonal.loc[\"Increasing Claim Case\", \"Reported Claims\"]\n", + " )\n", + " })\n", " col20 = pd.DataFrame({\"Actual IBNR\": col_diff(col18, col19)})\n", "\n", " df1 = pd.concat([col1, col2], axis=1)\n", @@ -260,57 +306,72 @@ " dfs_upper = [df1, df2, df3]\n", " dfs_lower = [df4, df5, df6]\n", "\n", - " results_upper = pd.concat(dfs_upper, axis=1, keys=[\"\", \"Steady State\", \"Increasing Claim Ratios\"])\n", - " results_upper = results_upper.set_index((\"\", \"Accident Year\")) # since its multi-index, I needed to use the tuple to designate the index col\n", - " results_upper.index.name = \"Accident Year\" \n", + " results_upper = pd.concat(\n", + " dfs_upper, axis=1, keys=[\"\", \"Steady State\", \"Increasing Claim Ratios\"]\n", + " )\n", + " results_upper = results_upper.set_index((\n", + " \"\",\n", + " \"Accident Year\",\n", + " )) # since its multi-index, I needed to use the tuple to designate the index col\n", + " results_upper.index.name = \"Accident Year\"\n", " results_upper.loc[\"Total\"] = results_upper.sum()\n", "\n", " # remove the totals from the ratio columns (where totals do not make sense)\n", " results_upper.loc[\"Total\", (\"Steady State\", \"Ult. Claim Ratio\")] = np.nan\n", " results_upper.loc[\"Total\", (\"Increasing Claim Ratios\", \"Ult. Claim Ratio\")] = np.nan\n", "\n", - " results_lower = pd.concat(dfs_lower, axis=1, keys=[\"\", \"Increasing Case Outstanding Strength\", \"Increasing Claim Ratios and Case Outstanding Strength\"])\n", - " results_lower = results_lower.set_index((\"\", \"Accident Year\")) # since its multi-index, I needed to use the tuple to designate the index col\n", - " results_lower.index.name = \"Accident Year\" \n", + " results_lower = pd.concat(\n", + " dfs_lower,\n", + " axis=1,\n", + " keys=[\n", + " \"\",\n", + " \"Increasing Case Outstanding Strength\",\n", + " \"Increasing Claim Ratios and Case Outstanding Strength\",\n", + " ],\n", + " )\n", + " results_lower = results_lower.set_index((\n", + " \"\",\n", + " \"Accident Year\",\n", + " )) # since its multi-index, I needed to use the tuple to designate the index col\n", + " results_lower.index.name = \"Accident Year\"\n", " results_lower.loc[\"Total\"] = results_lower.sum()\n", "\n", " # remove the totals from the ratio columns (where totals do not make sense)\n", - " results_lower.loc[\"Total\", (\"Increasing Case Outstanding Strength\", \"Ult. Claim Ratio\")] = np.nan\n", - " results_lower.loc[\"Total\", (\"Increasing Claim Ratios and Case Outstanding Strength\", \"Ult. Claim Ratio\")] = np.nan\n", + " results_lower.loc[\n", + " \"Total\", (\"Increasing Case Outstanding Strength\", \"Ult. Claim Ratio\")\n", + " ] = np.nan\n", + " results_lower.loc[\n", + " \"Total\",\n", + " (\"Increasing Claim Ratios and Case Outstanding Strength\", \"Ult. Claim Ratio\"),\n", + " ] = np.nan\n", "\n", - " display(HTML(\"\"\"\n", + " display(\n", + " HTML(\"\"\"\n", "

\n", " Exhibit III Sheet 1: Summary of Earned Premium and Claim Ratio Assumptions and Actual IBNR\n", "

\n", - " \"\"\"))\n", - " \n", + " \"\"\")\n", + " )\n", + "\n", " upper_formats = {\n", " (\"Steady State\", \"Ult. Claim Ratio\"): \"{:.1%}\",\n", " (\"Steady State\", \"Ult. Claims\"): \"{:,.0f}\",\n", " (\"Steady State\", \"Rep. Claims 12/31/08\"): \"{:,.0f}\",\n", " (\"Steady State\", \"Actual IBNR\"): \"{:,.0f}\",\n", - "\n", " (\"Increasing Claim Ratios\", \"Ult. Claim Ratio\"): \"{:.1%}\",\n", " (\"Increasing Claim Ratios\", \"Ult. Claims\"): \"{:,.0f}\",\n", " (\"Increasing Claim Ratios\", \"Rep. Claims 12/31/08\"): \"{:,.0f}\",\n", " (\"Increasing Claim Ratios\", \"Actual IBNR\"): \"{:,.0f}\",\n", - "\n", " (\"\", \"Earned Premium\"): \"{:,.0f}\",\n", " }\n", "\n", - " display(\n", - " results_upper.style.format(\n", - " upper_formats,\n", - " na_rep=\"\"\n", - " )\n", - " )\n", - " \n", + " display(results_upper.style.format(upper_formats, na_rep=\"\"))\n", + "\n", " lower_formats = {\n", " (\"Increasing Case Outstanding Strength\", \"Ult. Claim Ratio\"): \"{:.1%}\",\n", " (\"Increasing Case Outstanding Strength\", \"Ult. Claims\"): \"{:,.0f}\",\n", " (\"Increasing Case Outstanding Strength\", \"Rep. Claims 12/31/08\"): \"{:,.0f}\",\n", " (\"Increasing Case Outstanding Strength\", \"Actual IBNR\"): \"{:,.0f}\",\n", - "\n", " (\n", " \"Increasing Claim Ratios and Case Outstanding Strength\",\n", " \"Ult. Claim Ratio\",\n", @@ -327,16 +388,10 @@ " \"Increasing Claim Ratios and Case Outstanding Strength\",\n", " \"Actual IBNR\",\n", " ): \"{:,.0f}\",\n", - "\n", " (\"\", \"Earned Premium\"): \"{:,.0f}\",\n", " }\n", "\n", - " display(\n", - " results_lower.style.format(\n", - " lower_formats,\n", - " na_rep=\"\"\n", - " )\n", - " )\n", + " display(results_lower.style.format(lower_formats, na_rep=\"\"))\n", "\n", " return (results_upper, results_lower)" ] @@ -388,21 +443,15 @@ }, "outputs": [], "source": [ - "dev_input = {\n", - " \"average\": \"volume\",\n", - " \"n_periods\": 5\n", - "}\n", + "dev_input = {\"average\": \"volume\", \"n_periods\": 5}\n", "\n", - "tail_input = {\n", - " \"tail\": 1.0,\n", - " \"projection_period\": -1\n", - "}\n", + "tail_input = {\"tail\": 1.0, \"projection_period\": -1}\n", "\n", "res_up, res_low = ex3sht1(\n", " triangles,\n", " dev_input,\n", " tail_input,\n", - ")\n" + ")" ] }, { @@ -546,9 +595,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Steady State\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5', tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -572,8 +622,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.169, 1.056, 1.032, 1.010, 1.000,\n", - " 1.010, 1.000, 1.000, 1.000, 1.000,\n", + " 1.169,\n", + " 1.056,\n", + " 1.032,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -615,10 +673,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Steady State\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -642,8 +700,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.690, 1.183, 1.095, 1.043, 1.021,\n", - " 1.010, 1.000, 1.010, 1.000, 1.000,\n", + " 1.690,\n", + " 1.183,\n", + " 1.095,\n", + " 1.043,\n", + " 1.021,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -685,10 +751,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Claim\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -712,8 +778,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.169, 1.056, 1.032, 1.010, 1.000,\n", - " 1.010, 1.000, 1.000, 1.000, 1.000,\n", + " 1.169,\n", + " 1.056,\n", + " 1.032,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -755,10 +829,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Claim\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -782,8 +856,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.690, 1.183, 1.095, 1.043, 1.021,\n", - " 1.010, 1.000, 1.010, 1.000, 1.000,\n", + " 1.690,\n", + " 1.183,\n", + " 1.095,\n", + " 1.043,\n", + " 1.021,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -825,10 +907,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Case\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -852,8 +934,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.178, 1.061, 1.034, 1.009, 1.000,\n", - " 1.010, 1.000, 1.000, 1.000, 1.000,\n", + " 1.178,\n", + " 1.061,\n", + " 1.034,\n", + " 1.009,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -895,10 +985,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Case\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -922,8 +1012,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.690, 1.183, 1.095, 1.043, 1.021,\n", - " 1.010, 1.000, 1.010, 1.000, 1.000,\n", + " 1.690,\n", + " 1.183,\n", + " 1.095,\n", + " 1.043,\n", + " 1.021,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -965,10 +1063,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Claim Case\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -992,8 +1090,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.179, 1.061, 1.035, 1.009, 1.000,\n", - " 1.010, 1.000, 1.000, 1.000, 1.000,\n", + " 1.179,\n", + " 1.061,\n", + " 1.035,\n", + " 1.009,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -1035,10 +1141,10 @@ "source": [ "devs = dev_exhibit(\n", " triangles.loc[\"Increasing Claim Case\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -1062,8 +1168,16 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.690, 1.183, 1.095, 1.043, 1.021,\n", - " 1.010, 1.000, 1.010, 1.000, 1.000,\n", + " 1.690,\n", + " 1.183,\n", + " 1.095,\n", + " 1.043,\n", + " 1.021,\n", + " 1.010,\n", + " 1.000,\n", + " 1.010,\n", + " 1.000,\n", + " 1.000,\n", " ]),\n", " atol=0.0005,\n", " rtol=0,\n", @@ -1121,60 +1235,53 @@ "\n", " tri = tr.copy()\n", "\n", - " dev_input = {\n", - " \"average\": \"volume\",\n", - " \"n_periods\": 5\n", - " }\n", + " dev_input = {\"average\": \"volume\", \"n_periods\": 5}\n", "\n", - " tail_input = {\n", - " \"tail\": 1.0,\n", - " \"projection_period\": -1\n", - " }\n", + " tail_input = {\"tail\": 1.0, \"projection_period\": -1}\n", "\n", " dev = cl.Development(**dev_input)\n", " tail = cl.TailConstant(**tail_input)\n", " tri = tail.fit_transform(dev.fit_transform(tri))\n", "\n", " actual_scenario = (\n", - " \"Steady State\"\n", - " if scenario in [\"Steady State\", \"Increasing Case\"]\n", - " else \"Increasing Claim\"\n", + " \"Steady State\"\n", + " if scenario in [\"Steady State\", \"Increasing Case\"]\n", + " else \"Increasing Claim\"\n", " )\n", "\n", " cdf = format_col(tri.cdf_.loc[actual_scenario][\"Reported Claims\"], reverse=True)\n", " ult = format_col(tri.loc[actual_scenario][\"Reported Claims\"].latest_diagonal)\n", - " \n", - " col1 = pd.DataFrame({\n", - " \"Accident Year\": format_col(tr.origin)\n", - " })\n", + "\n", + " col1 = pd.DataFrame({\"Accident Year\": format_col(tr.origin)})\n", "\n", " col2 = pd.DataFrame({\n", " \"Age of Accident Year at 12/31/08\": format_col(tr.development, reverse=True)\n", - "\n", " })\n", "\n", " col3 = pd.DataFrame({\n", - " \"Claims at 12/31/2008 - Reported\": format_col(tri.loc[scenario][\"Reported Claims\"].latest_diagonal)\n", - "\n", + " \"Claims at 12/31/2008 - Reported\": format_col(\n", + " tri.loc[scenario][\"Reported Claims\"].latest_diagonal\n", + " )\n", " })\n", "\n", " col4 = pd.DataFrame({\n", - " \"Claims at 12/31/2008 - Paid\": format_col(tri.loc[scenario][\"Paid Claims\"].latest_diagonal)\n", - "\n", + " \"Claims at 12/31/2008 - Paid\": format_col(\n", + " tri.loc[scenario][\"Paid Claims\"].latest_diagonal\n", + " )\n", " })\n", "\n", - " col5 = pd.DataFrame({\n", - " \"Case Outstanding\": col_diff(col3, col4)\n", - "\n", - " })\n", + " col5 = pd.DataFrame({\"Case Outstanding\": col_diff(col3, col4)})\n", "\n", " col6 = pd.DataFrame({\n", - " \"CDF to Ult. - Reported\": format_col(tri.cdf_.loc[scenario][\"Reported Claims\"], reverse=True)\n", - "\n", + " \"CDF to Ult. - Reported\": format_col(\n", + " tri.cdf_.loc[scenario][\"Reported Claims\"], reverse=True\n", + " )\n", " })\n", "\n", " col7 = pd.DataFrame({\n", - " \"CDF to Ult. - Paid\": format_col(tri.cdf_.loc[scenario][\"Paid Claims\"], reverse=True)\n", + " \"CDF to Ult. - Paid\": format_col(\n", + " tri.cdf_.loc[scenario][\"Paid Claims\"], reverse=True\n", + " )\n", " })\n", "\n", " col8 = pd.DataFrame({\n", @@ -1193,20 +1300,30 @@ " \"Estimated IBNR Using Dev. Method - Paid\": col_diff(col9, col3)\n", " })\n", "\n", - " col12 = pd.DataFrame({\n", - " \"Actual IBNR\": col_diff(col_mult(ult, cdf), col3)\n", - " })\n", + " col12 = pd.DataFrame({\"Actual IBNR\": col_diff(col_mult(ult, cdf), col3)})\n", "\n", " col13 = pd.DataFrame({\n", " \"Difference from Actual IBNR - Reported\": col_diff(col12, col10)\n", " })\n", "\n", - " col14 = pd.DataFrame({\n", - " \"Difference from Actual IBNR - Paid\": col_diff(col12, col11)\n", - " })\n", - "\n", - "\n", - " cols = [col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14]\n", + " col14 = pd.DataFrame({\"Difference from Actual IBNR - Paid\": col_diff(col12, col11)})\n", + "\n", + " cols = [\n", + " col1,\n", + " col2,\n", + " col3,\n", + " col4,\n", + " col5,\n", + " col6,\n", + " col7,\n", + " col8,\n", + " col9,\n", + " col10,\n", + " col11,\n", + " col12,\n", + " col13,\n", + " col14,\n", + " ]\n", "\n", " results = pd.concat(cols, axis=1)\n", "\n", @@ -1222,10 +1339,8 @@ " \"Actual IBNR\": \"{:,.0f}\",\n", " \"Difference from Actual IBNR - Reported\": \"{:,.0f}\",\n", " \"Difference from Actual IBNR - Paid\": \"{:,.0f}\",\n", - "\n", " # Ages\n", " \"Age of Accident Year at 12/31/08\": \"{:.0f}\",\n", - "\n", " # Factors\n", " \"CDF to Ult. - Reported\": \"{:.3f}\",\n", " \"CDF to Ult. - Paid\": \"{:.3f}\",\n", @@ -1245,17 +1360,14 @@ " \"Difference from Actual IBNR - Reported\",\n", " \"Difference from Actual IBNR - Paid\",\n", " ]\n", - " \n", + "\n", " results.loc[\"Total\", total_cols] = results[total_cols].sum()\n", "\n", " display(HTML(f\"

Exhibit III Sheets 10 and 11 — {scenario}

\"))\n", "\n", - " display(\n", - " results.style.format(format_dict, na_rep=\"\")\n", - " )\n", + " display(results.style.format(format_dict, na_rep=\"\"))\n", "\n", - " return results\n", - "\n" + " return results" ] }, { @@ -1295,7 +1407,7 @@ "\n", "res_3 = Ex3Sht10(\"Increasing Case\", triangles)\n", "\n", - "res_4 = Ex3Sht10(\"Increasing Claim Case\", triangles)\n" + "res_4 = Ex3Sht10(\"Increasing Claim Case\", triangles)" ] }, { @@ -1318,9 +1430,9 @@ "source": [ "estimate_cols = [\n", " \"Projected Ult. Claims Using Dev. Method - Reported\", # (8)\n", - " \"Projected Ult. Claims Using Dev. Method - Paid\", # (9)\n", - " \"Estimated IBNR Using Dev. Method - Reported\", # (10)\n", - " \"Estimated IBNR Using Dev. Method - Paid\", # (11)\n", + " \"Projected Ult. Claims Using Dev. Method - Paid\", # (9)\n", + " \"Estimated IBNR Using Dev. Method - Reported\", # (10)\n", + " \"Estimated IBNR Using Dev. Method - Paid\", # (11)\n", "]\n", "\n", "# ruff: noqa: E241\n", @@ -1330,17 +1442,17 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [700000, 700000, 0, 0],\n", - " [735000, 735000, 0, 0],\n", - " [771750, 771750, 0, 0],\n", - " [810338, 810338, 0, 0],\n", - " [850854, 850854, 8509, 8509],\n", - " [893397, 893397, 8934, 8934],\n", - " [938067, 938067, 18761, 18761],\n", - " [984970, 984970, 49249, 49249],\n", - " [1034219, 1034219, 103422, 103422],\n", - " [1085930, 1085930, 249764, 249764],\n", - "]),\n", + " [700000, 700000, 0, 0],\n", + " [735000, 735000, 0, 0],\n", + " [771750, 771750, 0, 0],\n", + " [810338, 810338, 0, 0],\n", + " [850854, 850854, 8509, 8509],\n", + " [893397, 893397, 8934, 8934],\n", + " [938067, 938067, 18761, 18761],\n", + " [984970, 984970, 49249, 49249],\n", + " [1034219, 1034219, 103422, 103422],\n", + " [1085930, 1085930, 249764, 249764],\n", + " ]),\n", " atol=3,\n", " rtol=0,\n", ")\n", @@ -1351,17 +1463,17 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [700000, 700000, 0, 0],\n", - " [735000, 735000, 0, 0],\n", - " [771750, 771750, 0, 0],\n", - " [810338, 810338, 0, 0],\n", - " [850854, 850854, 8509, 8509],\n", - " [1021025, 1021025, 10210, 10210],\n", - " [1139081, 1139081, 22782, 22782],\n", - " [1266390, 1266390, 63320, 63320],\n", - " [1403583, 1403583, 140358, 140358],\n", - " [1551328, 1551328, 356805, 356805],\n", - "]),\n", + " [700000, 700000, 0, 0],\n", + " [735000, 735000, 0, 0],\n", + " [771750, 771750, 0, 0],\n", + " [810338, 810338, 0, 0],\n", + " [850854, 850854, 8509, 8509],\n", + " [1021025, 1021025, 10210, 10210],\n", + " [1139081, 1139081, 22782, 22782],\n", + " [1266390, 1266390, 63320, 63320],\n", + " [1403583, 1403583, 140358, 140358],\n", + " [1551328, 1551328, 356805, 356805],\n", + " ]),\n", " atol=3,\n", " rtol=0,\n", ")\n", @@ -1372,17 +1484,17 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [700000, 700000, 0, 0],\n", - " [735000, 735000, 0, 0],\n", - " [771750, 771750, 0, 0],\n", - " [810338, 810338, 0, 0],\n", - " [850854, 850854, 8509, 8509],\n", - " [893397, 893397, 8934, 8934],\n", - " [951656, 938067, 18279, 4690],\n", - " [1015302, 984970, 52493, 22162],\n", - " [1096235, 1034219, 116313, 54296],\n", - " [1227589, 1085930, 296404, 154745],\n", - "]),\n", + " [700000, 700000, 0, 0],\n", + " [735000, 735000, 0, 0],\n", + " [771750, 771750, 0, 0],\n", + " [810338, 810338, 0, 0],\n", + " [850854, 850854, 8509, 8509],\n", + " [893397, 893397, 8934, 8934],\n", + " [951656, 938067, 18279, 4690],\n", + " [1015302, 984970, 52493, 22162],\n", + " [1096235, 1034219, 116313, 54296],\n", + " [1227589, 1085930, 296404, 154745],\n", + " ]),\n", " atol=3,\n", " rtol=0,\n", ")\n", @@ -1393,20 +1505,20 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [700000, 700000, 0, 0],\n", - " [735000, 735000, 0, 0],\n", - " [771750, 771750, 0, 0],\n", - " [810338, 810338, 0, 0],\n", - " [850854, 850854, 8509, 8509],\n", - " [1021025, 1021025, 10210, 10210],\n", - " [1155482, 1139081, 22096, 5695],\n", - " [1305639, 1266390, 67742, 28494],\n", - " [1488874, 1403583, 158980, 73688],\n", - " [1756504, 1551328, 426240, 221064],\n", - "]),\n", + " [700000, 700000, 0, 0],\n", + " [735000, 735000, 0, 0],\n", + " [771750, 771750, 0, 0],\n", + " [810338, 810338, 0, 0],\n", + " [850854, 850854, 8509, 8509],\n", + " [1021025, 1021025, 10210, 10210],\n", + " [1155482, 1139081, 22096, 5695],\n", + " [1305639, 1266390, 67742, 28494],\n", + " [1488874, 1403583, 158980, 73688],\n", + " [1756504, 1551328, 426240, 221064],\n", + " ]),\n", " atol=3,\n", " rtol=0,\n", - ")\n" + ")" ] }, { @@ -1504,71 +1616,51 @@ "\n", " tri = tri.copy()\n", "\n", - " dev_input = {\n", - " \"average\": \"volume\",\n", - " \"n_periods\": 5\n", - " }\n", + " dev_input = {\"average\": \"volume\", \"n_periods\": 5}\n", "\n", - " tail_input = {\n", - " \"tail\": 1.0,\n", - " \"projection_period\": -1\n", - " }\n", + " tail_input = {\"tail\": 1.0, \"projection_period\": -1}\n", "\n", " dev = cl.Development(**dev_input)\n", " tail = cl.TailConstant(**tail_input)\n", " tri = tail.fit_transform(dev.fit_transform(tri))\n", "\n", - " col1 = pd.DataFrame({\n", - " \"Accident Year\": format_col(tri.origin)\n", - " })\n", + " col1 = pd.DataFrame({\"Accident Year\": format_col(tri.origin)})\n", "\n", " col2 = pd.DataFrame({\n", - " \"Priv Pass Auto\": format_col(tri.loc[\"Steady State\"][\"Earned Premium\"].latest_diagonal / 2)\n", - " })\n", + " \"Priv Pass Auto\": format_col(\n", + " tri.loc[\"Steady State\"][\"Earned Premium\"].latest_diagonal / 2\n", + " )\n", + " })\n", "\n", " col3 = pd.DataFrame({\n", - " \"Comm Auto\": col_diff(\n", - " format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal),\n", - " col2\n", - " )\n", - " })\n", + " \"Comm Auto\": col_diff(\n", + " format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal), col2\n", + " )\n", + " })\n", "\n", " col4 = pd.DataFrame({\n", - " \"Total\": format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal)\n", - " })\n", + " \"Total\": format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal)\n", + " })\n", "\n", - " col5 = pd.DataFrame({\n", - " \"Priv Pass Auto (%)\": pd.Series([0.7] * 10) \n", - " })\n", + " col5 = pd.DataFrame({\"Priv Pass Auto (%)\": pd.Series([0.7] * 10)})\n", "\n", - " col6 = pd.DataFrame({\n", - " \"Comm Auto (%)\": pd.Series([0.8] * 10) \n", - " })\n", + " col6 = pd.DataFrame({\"Comm Auto (%)\": pd.Series([0.8] * 10)})\n", "\n", - " col8 = pd.DataFrame({\n", - " \"Priv Pass Auto\": col_mult(col2, col5) \n", - " })\n", + " col8 = pd.DataFrame({\"Priv Pass Auto\": col_mult(col2, col5)})\n", "\n", - " col9 = pd.DataFrame({\n", - " \"Comm Auto\": col_mult(col3, col6) \n", - " })\n", + " col9 = pd.DataFrame({\"Comm Auto\": col_mult(col3, col6)})\n", "\n", - " col10 = pd.DataFrame({\n", - " \"Comm Auto\": col_add(col8, col9) \n", - " })\n", + " col10 = pd.DataFrame({\"Comm Auto\": col_add(col8, col9)})\n", "\n", - " col7 = pd.DataFrame({\n", - " \"Total (%)\": col_div(col10, col4) \n", - " })\n", + " col7 = pd.DataFrame({\"Total (%)\": col_div(col10, col4)})\n", "\n", " col11 = pd.DataFrame({\n", - " \"Reported Claims as at 12/31/2008\": format_col(tri.loc[scenario][\"Reported Claims\"].latest_diagonal) \n", - " })\n", - "\n", - " col12 = pd.DataFrame({\n", - " \"Actual IBNR\": col_diff(col10, col11) \n", - " })\n", + " \"Reported Claims as at 12/31/2008\": format_col(\n", + " tri.loc[scenario][\"Reported Claims\"].latest_diagonal\n", + " )\n", + " })\n", "\n", + " col12 = pd.DataFrame({\"Actual IBNR\": col_diff(col10, col11)})\n", "\n", " cols = [col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12]\n", "\n", @@ -1581,12 +1673,10 @@ " \"Total\": \"{:,.0f}\",\n", " \"Reported Claims as at 12/31/2008\": \"{:,.0f}\",\n", " \"Actual IBNR\": \"{:,.0f}\",\n", - "\n", " # Percentages\n", " \"Priv Pass Auto (%)\": \"{:.1%}\",\n", " \"Comm Auto (%)\": \"{:.1%}\",\n", " \"Total (%)\": \"{:.1%}\",\n", - "\n", " # Factors\n", " \"CDF to Ult. - Reported\": \"{:.3f}\",\n", " \"CDF to Ult. - Paid\": \"{:.3f}\",\n", @@ -1613,7 +1703,7 @@ " \"props\": [\n", " (\"white-space\", \"normal\"),\n", " (\"max-width\", \"80px\"),\n", - " ]\n", + " ],\n", " }\n", " ])\n", " )\n", @@ -1654,7 +1744,7 @@ "source": [ "res_1 = Ex4Sht1(\"Steady State\", data)\n", "\n", - "res_2 = Ex4Sht1(\"Changing Product Mix\", data)\n" + "res_2 = Ex4Sht1(\"Changing Product Mix\", data)" ] }, { @@ -1678,16 +1768,16 @@ "assert np.allclose(\n", " res_1.iloc[:-1, [8, 10]].values,\n", " np.array([\n", - " [1500000, 0],\n", - " [1575000, 0],\n", - " [1653750, 0],\n", - " [1736438, 0],\n", - " [1823259, 8509],\n", - " [1914422, 29354],\n", - " [2010143, 61644],\n", - " [2110651, 173073],\n", - " [2216183, 363454],\n", - " [2326992, 758599],\n", + " [1500000, 0],\n", + " [1575000, 0],\n", + " [1653750, 0],\n", + " [1736438, 0],\n", + " [1823259, 8509],\n", + " [1914422, 29354],\n", + " [2010143, 61644],\n", + " [2110651, 173073],\n", + " [2216183, 363454],\n", + " [2326992, 758599],\n", " ]),\n", " atol=2,\n", " rtol=0,\n", @@ -1696,15 +1786,15 @@ "assert np.allclose(\n", " res_2.iloc[:-1, [8, 10]].values,\n", " np.array([\n", - " [1500000, 0],\n", - " [1575000, 0],\n", - " [1653750, 0],\n", - " [1736438, 0],\n", - " [1823259, 8509],\n", - " [1914422, 29354],\n", - " [2265400, 71855],\n", - " [2710503, 239057],\n", - " [3277411, 596924],\n", + " [1500000, 0],\n", + " [1575000, 0],\n", + " [1653750, 0],\n", + " [1736438, 0],\n", + " [1823259, 8509],\n", + " [1914422, 29354],\n", + " [2265400, 71855],\n", + " [2710503, 239057],\n", + " [3277411, 596924],\n", " [4002080, 1445385],\n", " ]),\n", " atol=2,\n", @@ -1747,10 +1837,10 @@ "source": [ "devs = dev_exhibit(\n", " data.loc[\"Steady State\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -1774,9 +1864,17 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.240, 1.098, 1.056, 1.016, 1.011,\n", - " 1.005, 1.000, 1.000, 1.000, 1.000,\n", - "]),\n", + " 1.240,\n", + " 1.098,\n", + " 1.056,\n", + " 1.016,\n", + " 1.011,\n", + " 1.005,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " ]),\n", " atol=0.0005,\n", " rtol=0,\n", ")" @@ -1817,10 +1915,10 @@ "source": [ "devs = dev_exhibit(\n", " data.loc[\"Steady State\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -1844,9 +1942,17 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.840, 1.299, 1.157, 1.077, 1.033,\n", - " 1.016, 1.005, 1.010, 1.005, 1.000,\n", - "]),\n", + " 1.840,\n", + " 1.299,\n", + " 1.157,\n", + " 1.077,\n", + " 1.033,\n", + " 1.016,\n", + " 1.005,\n", + " 1.010,\n", + " 1.005,\n", + " 1.000,\n", + " ]),\n", " atol=0.0005,\n", " rtol=0,\n", ")" @@ -1887,10 +1993,10 @@ "source": [ "devs = dev_exhibit(\n", " data.loc[\"Changing Product Mix\", \"Reported Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -1914,9 +2020,17 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.252, 1.101, 1.057, 1.016, 1.011,\n", - " 1.005, 1.000, 1.000, 1.000, 1.000,\n", - "]),\n", + " 1.252,\n", + " 1.101,\n", + " 1.057,\n", + " 1.016,\n", + " 1.011,\n", + " 1.005,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " 1.000,\n", + " ]),\n", " atol=0.0005,\n", " rtol=0,\n", ")" @@ -1957,10 +2071,10 @@ "source": [ "devs = dev_exhibit(\n", " data.loc[\"Changing Product Mix\", \"Paid Claims\"],\n", - " avg_params={\"volume_5\": {'n_periods': 5, 'average': 'volume'}},\n", - " selected_avg='volume_5',\n", - " tail=1\n", - " )" + " avg_params={\"volume_5\": {\"n_periods\": 5, \"average\": \"volume\"}},\n", + " selected_avg=\"volume_5\",\n", + " tail=1,\n", + ")" ] }, { @@ -1984,9 +2098,17 @@ "assert np.allclose(\n", " devs[\"Selected\"].ldf_.round(3).values.squeeze(),\n", " np.array([\n", - " 1.870, 1.310, 1.158, 1.077, 1.033,\n", - " 1.016, 1.005, 1.010, 1.005, 1.000,\n", - "]),\n", + " 1.870,\n", + " 1.310,\n", + " 1.158,\n", + " 1.077,\n", + " 1.033,\n", + " 1.016,\n", + " 1.005,\n", + " 1.010,\n", + " 1.005,\n", + " 1.000,\n", + " ]),\n", " atol=0.0005,\n", " rtol=0,\n", ")" @@ -2039,56 +2161,48 @@ " return x.squeeze() * y.squeeze()\n", "\n", " def col_sum(x, y):\n", - " return x.squeeze() + y.squeeze()\n", + " return x.squeeze() + y.squeeze()\n", "\n", " tri = tr.copy()\n", "\n", - " dev_input = {\n", - " \"average\": \"volume\",\n", - " \"n_periods\": 5\n", - " }\n", + " dev_input = {\"average\": \"volume\", \"n_periods\": 5}\n", "\n", - " tail_input = {\n", - " \"tail\": 1.0,\n", - " \"projection_period\": -1\n", - " }\n", + " tail_input = {\"tail\": 1.0, \"projection_period\": -1}\n", "\n", " dev = cl.Development(**dev_input)\n", " tail = cl.TailConstant(**tail_input)\n", " tri = tail.fit_transform(dev.fit_transform(tri))\n", "\n", - " \n", - " col1 = pd.DataFrame({\n", - " \"Accident Year\": format_col(tr.origin)\n", - " })\n", + " col1 = pd.DataFrame({\"Accident Year\": format_col(tr.origin)})\n", "\n", " col2 = pd.DataFrame({\n", " \"Age of Accident Year at 12/31/08\": format_col(tr.development, reverse=True)\n", - "\n", " })\n", "\n", " col3 = pd.DataFrame({\n", - " \"Claims at 12/31/2008 - Reported\": format_col(tri.loc[scenario][\"Reported Claims\"].latest_diagonal)\n", - "\n", + " \"Claims at 12/31/2008 - Reported\": format_col(\n", + " tri.loc[scenario][\"Reported Claims\"].latest_diagonal\n", + " )\n", " })\n", "\n", " col4 = pd.DataFrame({\n", - " \"Claims at 12/31/2008 - Paid\": format_col(tri.loc[scenario][\"Paid Claims\"].latest_diagonal)\n", - "\n", + " \"Claims at 12/31/2008 - Paid\": format_col(\n", + " tri.loc[scenario][\"Paid Claims\"].latest_diagonal\n", + " )\n", " })\n", "\n", - " col5 = pd.DataFrame({\n", - " \"Case Outstanding\": col_diff(col3, col4)\n", - "\n", - " })\n", + " col5 = pd.DataFrame({\"Case Outstanding\": col_diff(col3, col4)})\n", "\n", " col6 = pd.DataFrame({\n", - " \"CDF to Ult. - Reported\": format_col(tri.cdf_.loc[scenario][\"Reported Claims\"], reverse=True)\n", - "\n", + " \"CDF to Ult. - Reported\": format_col(\n", + " tri.cdf_.loc[scenario][\"Reported Claims\"], reverse=True\n", + " )\n", " })\n", "\n", " col7 = pd.DataFrame({\n", - " \"CDF to Ult. - Paid\": format_col(tri.cdf_.loc[scenario][\"Paid Claims\"], reverse=True)\n", + " \"CDF to Ult. - Paid\": format_col(\n", + " tri.cdf_.loc[scenario][\"Paid Claims\"], reverse=True\n", + " )\n", " })\n", "\n", " col8 = pd.DataFrame({\n", @@ -2109,34 +2223,39 @@ "\n", " colA = col_mult(\n", " format_col(tri.loc[\"Steady State\"][\"Earned Premium\"].latest_diagonal / 2),\n", - " pd.Series([0.7] * 10)\n", - " )\n", + " pd.Series([0.7] * 10),\n", + " )\n", " colB = col_diff(\n", - " format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal), \n", - " format_col(tri.loc[\"Steady State\"][\"Earned Premium\"].latest_diagonal / 2)\n", - " ) * pd.Series([0.8] * 10)\n", + " format_col(tri.loc[scenario][\"Earned Premium\"].latest_diagonal),\n", + " format_col(tri.loc[\"Steady State\"][\"Earned Premium\"].latest_diagonal / 2),\n", + " ) * pd.Series([0.8] * 10)\n", " colC = format_col(tri.loc[scenario][\"Reported Claims\"].latest_diagonal)\n", - " colD = col_diff(\n", - " col_sum(\n", - " colA, \n", - " colB\n", - " ), \n", - " colC)\n", + " colD = col_diff(col_sum(colA, colB), colC)\n", "\n", - " col12 = pd.DataFrame({\n", - " \"Actual IBNR\": format_col(colD) \n", - " })\n", + " col12 = pd.DataFrame({\"Actual IBNR\": format_col(colD)})\n", "\n", " col13 = pd.DataFrame({\n", " \"Difference from Actual IBNR - Reported\": col_diff(col12, col10)\n", " })\n", "\n", - " col14 = pd.DataFrame({\n", - " \"Difference from Actual IBNR - Paid\": col_diff(col12, col11)\n", - " })\n", - "\n", - "\n", - " cols = [col1, col2, col3, col4, col5, col6, col7, col8, col9, col10, col11, col12, col13, col14]\n", + " col14 = pd.DataFrame({\"Difference from Actual IBNR - Paid\": col_diff(col12, col11)})\n", + "\n", + " cols = [\n", + " col1,\n", + " col2,\n", + " col3,\n", + " col4,\n", + " col5,\n", + " col6,\n", + " col7,\n", + " col8,\n", + " col9,\n", + " col10,\n", + " col11,\n", + " col12,\n", + " col13,\n", + " col14,\n", + " ]\n", "\n", " results = pd.concat(cols, axis=1)\n", "\n", @@ -2152,10 +2271,8 @@ " \"Actual IBNR\": \"{:,.0f}\",\n", " \"Difference from Actual IBNR - Reported\": \"{:,.0f}\",\n", " \"Difference from Actual IBNR - Paid\": \"{:,.0f}\",\n", - "\n", " # Ages\n", " \"Age of Accident Year at 12/31/08\": \"{:.0f}\",\n", - "\n", " # Factors\n", " \"CDF to Ult. - Reported\": \"{:.3f}\",\n", " \"CDF to Ult. - Paid\": \"{:.3f}\",\n", @@ -2175,18 +2292,14 @@ " \"Difference from Actual IBNR - Reported\",\n", " \"Difference from Actual IBNR - Paid\",\n", " ]\n", - " \n", + "\n", " results.loc[\"Total\", total_cols] = results[total_cols].sum()\n", "\n", " display(HTML(f\"

Exhibit IV Sheet 6 — {scenario}

\"))\n", "\n", - " display(\n", - " results.style.format(format_dict, na_rep=\"\")\n", - " )\n", - "\n", - " \n", + " display(results.style.format(format_dict, na_rep=\"\"))\n", "\n", - " return results\n" + " return results" ] }, { @@ -2222,7 +2335,7 @@ "source": [ "res_1 = Ex4Sht6(\"Steady State\", data)\n", "\n", - "res_2 = Ex4Sht6(\"Changing Product Mix\", data)\n" + "res_2 = Ex4Sht6(\"Changing Product Mix\", data)" ] }, { @@ -2256,17 +2369,17 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [1500000, 1500000, 0, 0],\n", - " [1575000, 1575000, 0, 0],\n", - " [1653750, 1653750, 0, 0],\n", - " [1736438, 1736438, 0, 0],\n", - " [1823259, 1823259, 8509, 8509],\n", - " [1914422, 1914422, 29354, 29354],\n", - " [2010143, 2010143, 61644, 61644],\n", - " [2110651, 2110651, 173073, 173073],\n", - " [2216183, 2216183, 363454, 363454],\n", - " [2326992, 2326992, 758599, 758599],\n", - "]),\n", + " [1500000, 1500000, 0, 0],\n", + " [1575000, 1575000, 0, 0],\n", + " [1653750, 1653750, 0, 0],\n", + " [1736438, 1736438, 0, 0],\n", + " [1823259, 1823259, 8509, 8509],\n", + " [1914422, 1914422, 29354, 29354],\n", + " [2010143, 2010143, 61644, 61644],\n", + " [2110651, 2110651, 173073, 173073],\n", + " [2216183, 2216183, 363454, 363454],\n", + " [2326992, 2326992, 758599, 758599],\n", + " ]),\n", " atol=1,\n", " rtol=1e-5,\n", ")\n", @@ -2277,17 +2390,17 @@ " estimate_cols,\n", " ].values,\n", " np.array([\n", - " [1500000, 1500000, 0, 0],\n", - " [1575000, 1575000, 0, 0],\n", - " [1653750, 1653750, 0, 0],\n", - " [1736438, 1736438, 0, 0],\n", - " [1823259, 1823259, 8509, 8509],\n", - " [1914422, 1914422, 29354, 29354],\n", - " [2262942, 2251655, 69397, 58110],\n", - " [2693735, 2650749, 222289, 179303],\n", - " [3217775, 3091666, 537288, 411179],\n", - " [3842645, 3592939, 1285950, 1036245],\n", - "]),\n", + " [1500000, 1500000, 0, 0],\n", + " [1575000, 1575000, 0, 0],\n", + " [1653750, 1653750, 0, 0],\n", + " [1736438, 1736438, 0, 0],\n", + " [1823259, 1823259, 8509, 8509],\n", + " [1914422, 1914422, 29354, 29354],\n", + " [2262942, 2251655, 69397, 58110],\n", + " [2693735, 2650749, 222289, 179303],\n", + " [3217775, 3091666, 537288, 411179],\n", + " [3842645, 3592939, 1285950, 1036245],\n", + " ]),\n", " atol=1,\n", " rtol=1e-5,\n", ")" diff --git a/docs/friedland/chapter_8.ipynb b/docs/friedland/chapter_8.ipynb index 9d49fe823..0cbed9b18 100644 --- a/docs/friedland/chapter_8.ipynb +++ b/docs/friedland/chapter_8.ipynb @@ -1,5042 +1,5501 @@ { - "cells": [ + "cells": [ + { + "cell_type": "markdown", + "id": "e2483dec", + "metadata": {}, + "source": [ + "# Chapter 8 - Expected Claims Technique\n" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "f18b63df", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:54.161935Z", + "iopub.status.busy": "2026-07-23T16:32:54.161802Z", + "iopub.status.idle": "2026-07-23T16:32:57.252132Z", + "shell.execute_reply": "2026-07-23T16:32:57.251068Z" + } + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "import pandas as pd\n", + "import chainladder as cl\n", + "from IPython.display import display\n", + "\n", + "pd.set_option(\"display.max_columns\", None)\n", + "pd.set_option(\"display.width\", 1000)\n", + "\n", + "\n", + "# Helper functions, skip to the next section for actual exhibits\n", + "def as_series(tri):\n", + " s = tri.to_frame(origin_as_datetime=False).iloc[:, 0]\n", + " s.index = [int(getattr(i, \"year\", i)) for i in s.index]\n", + " return s\n", + "\n", + "\n", + "def avg_ex_high_low(values):\n", + " values = np.asarray(values, dtype=float).flatten()\n", + " return (values.sum() - values.max() - values.min()) / (len(values) - 2)\n", + "\n", + "\n", + "def unpaid_exhibit(reported, paid, expected):\n", + " out = pd.DataFrame(index=list(reported.origin.year))\n", + " out[\"Reported (2)\"] = as_series(reported.latest_diagonal).values\n", + " out[\"Paid (3)\"] = as_series(paid.latest_diagonal).values\n", + " out[\"Expected Claims (4)\"] = as_series(expected).values\n", + " out[\"Case Outstanding (5)\"] = out[\"Reported (2)\"] - out[\"Paid (3)\"]\n", + " out[\"IBNR (6)\"] = out[\"Expected Claims (4)\"] - out[\"Reported (2)\"]\n", + " out[\"Total Unpaid (7)\"] = out[\"Expected Claims (4)\"] - out[\"Paid (3)\"]\n", + " return out" + ] + }, + { + "cell_type": "markdown", + "id": "16398dc9", + "metadata": {}, + "source": [ + "## P140 (Exhibit I Sheet 1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "2618380c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.262396Z", + "iopub.status.busy": "2026-07-23T16:32:57.261697Z", + "iopub.status.idle": "2026-07-23T16:32:57.450905Z", + "shell.execute_reply": "2026-07-23T16:32:57.450503Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "e2483dec", - "metadata": {}, - "source": [ - "# Chapter 8 - Expected Claims Technique\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", + "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Trend to 7/1/08 (10)Tort Reform (11)Trended Adj Ult (12)Trended Adj Claim Ratio (13)
200010000000.09500000.01.0051.0510050000.09975000.010012500.024000000.02.9540.6719816540.00.83
20018000000.07200000.01.0201.158160000.08280000.08220000.018000000.02.5800.6714209092.00.79
20029400000.07600000.01.0301.259682000.09500000.09591000.019000000.02.2530.6714477710.00.76
200315600000.07800000.01.1001.3517160000.010530000.013845000.023000000.01.9680.6718255463.00.79
200416500000.011200000.01.2001.7519800000.019600000.019700000.032000000.01.7190.7525398225.00.79
200518500000.010200000.01.4002.5025900000.025500000.025700000.047000000.01.5011.0038575700.00.82
200616500000.06000000.01.8005.0029700000.030000000.029850000.050000000.01.3111.0039133350.00.78
200714000000.03000000.02.90015.0040600000.045000000.042800000.057000000.01.1451.0049006000.00.86
20088700000.0750000.04.00090.0034800000.067500000.051150000.062000000.01.0001.0051150000.00.82
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Trend to 7/1/08 (10) Tort Reform (11) Trended Adj Ult (12) Trended Adj Claim Ratio (13)\n", + "2000 10000000.0 9500000.0 1.005 1.05 10050000.0 9975000.0 10012500.0 24000000.0 2.954 0.67 19816540.0 0.83\n", + "2001 8000000.0 7200000.0 1.020 1.15 8160000.0 8280000.0 8220000.0 18000000.0 2.580 0.67 14209092.0 0.79\n", + "2002 9400000.0 7600000.0 1.030 1.25 9682000.0 9500000.0 9591000.0 19000000.0 2.253 0.67 14477710.0 0.76\n", + "2003 15600000.0 7800000.0 1.100 1.35 17160000.0 10530000.0 13845000.0 23000000.0 1.968 0.67 18255463.0 0.79\n", + "2004 16500000.0 11200000.0 1.200 1.75 19800000.0 19600000.0 19700000.0 32000000.0 1.719 0.75 25398225.0 0.79\n", + "2005 18500000.0 10200000.0 1.400 2.50 25900000.0 25500000.0 25700000.0 47000000.0 1.501 1.00 38575700.0 0.82\n", + "2006 16500000.0 6000000.0 1.800 5.00 29700000.0 30000000.0 29850000.0 50000000.0 1.311 1.00 39133350.0 0.78\n", + "2007 14000000.0 3000000.0 2.900 15.00 40600000.0 45000000.0 42800000.0 57000000.0 1.145 1.00 49006000.0 0.86\n", + "2008 8700000.0 750000.0 4.000 90.00 34800000.0 67500000.0 51150000.0 62000000.0 1.000 1.00 51150000.0 0.82" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 1, - "id": "f18b63df", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:54.161935Z", - "iopub.status.busy": "2026-07-23T16:32:54.161802Z", - "iopub.status.idle": "2026-07-23T16:32:57.252132Z", - "shell.execute_reply": "2026-07-23T16:32:57.251068Z" - } - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "import pandas as pd\n", - "import chainladder as cl\n", - "from IPython.display import display\n", - "\n", - "pd.set_option(\"display.max_columns\", None)\n", - "pd.set_option(\"display.width\", 1000)\n", - "\n", - "# Helper functions, skip to the next section for actual exhibits\n", - "def as_series(tri):\n", - " s = tri.to_frame(origin_as_datetime=False).iloc[:, 0]\n", - " s.index = [int(getattr(i, \"year\", i)) for i in s.index]\n", - " return s\n", - "\n", - "\n", - "def avg_ex_high_low(values):\n", - " values = np.asarray(values, dtype=float).flatten()\n", - " return (values.sum() - values.max() - values.min()) / (len(values) - 2)\n", - "\n", - "\n", - "def unpaid_exhibit(reported, paid, expected):\n", - " out = pd.DataFrame(index=list(reported.origin.year))\n", - " out[\"Reported (2)\"] = as_series(reported.latest_diagonal).values\n", - " out[\"Paid (3)\"] = as_series(paid.latest_diagonal).values\n", - " out[\"Expected Claims (4)\"] = as_series(expected).values\n", - " out[\"Case Outstanding (5)\"] = out[\"Reported (2)\"] - out[\"Paid (3)\"]\n", - " out[\"IBNR (6)\"] = out[\"Expected Claims (4)\"] - out[\"Reported (2)\"]\n", - " out[\"Total Unpaid (7)\"] = out[\"Expected Claims (4)\"] - out[\"Paid (3)\"]\n", - " return out\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", + "
Items (14)-(17)
Avg 2000-2005 (14)0.797
Avg 2000-2005 ex Hi/Lo (14)0.798
Avg 2001-2006 (14)0.788
Avg 2001-2006 ex Hi/Lo (14)0.787
Selected Claim Ratio (15)0.800
Expected Claims 2008 (16)49,600,000
Total Unpaid 2008 (17)48,850,000
IBNR 2008 (17)40,900,000
\n", + "
" + ], + "text/plain": [ + " Items (14)-(17)\n", + "Avg 2000-2005 (14) 0.797\n", + "Avg 2000-2005 ex Hi/Lo (14) 0.798\n", + "Avg 2001-2006 (14) 0.788\n", + "Avg 2001-2006 ex Hi/Lo (14) 0.787\n", + "Selected Claim Ratio (15) 0.800\n", + "Expected Claims 2008 (16) 49,600,000\n", + "Total Unpaid 2008 (17) 48,850,000\n", + "IBNR 2008 (17) 40,900,000" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "auto_bi = cl.load_sample(\"friedland_auto_bi_insurer\")\n", + "\n", + "reported_pattern = {\n", + " 12: 4,\n", + " 24: 2.9,\n", + " 36: 1.8,\n", + " 48: 1.4,\n", + " 60: 1.2,\n", + " 72: 1.1,\n", + " 84: 1.03,\n", + " 96: 1.02,\n", + " 108: 1.005,\n", + "}\n", + "paid_pattern = {\n", + " 12: 90,\n", + " 24: 15,\n", + " 36: 5,\n", + " 48: 2.5,\n", + " 60: 1.75,\n", + " 72: 1.35,\n", + " 84: 1.25,\n", + " 96: 1.15,\n", + " 108: 1.05,\n", + "}\n", + "\n", + "reported_bi = cl.DevelopmentConstant(\n", + " patterns=reported_pattern, style=\"cdf\"\n", + ").fit_transform(auto_bi[\"Reported Claims\"])\n", + "paid_bi = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", + " auto_bi[\"Paid Claims\"]\n", + ")\n", + "reported_ultimate = cl.Chainladder().fit(reported_bi).ultimate_\n", + "paid_ultimate = cl.Chainladder().fit(paid_bi).ultimate_\n", + "initial_selected = (reported_ultimate + paid_ultimate) / 2\n", + "\n", + "trend_factors = np.round(\n", + " cl\n", + " .Trend(trends=[0.145], dates=[(\"2008-12-31\", \"2000-01-01\")])\n", + " .fit(auto_bi[\"Earned Premium\"])\n", + " .trend_.latest_diagonal,\n", + " 3,\n", + ")\n", + "tort_factors = np.array([0.670, 0.670, 0.670, 0.670, 0.750, 1.0, 1.0, 1.0, 1.0])\n", + "trended_adj = np.round(\n", + " trend_factors * initial_selected * tort_factors.reshape(1, 1, -1, 1), 0\n", + ")\n", + "claim_ratio = np.round(trended_adj / auto_bi[\"Earned Premium\"].latest_diagonal, 2)\n", + "\n", + "years = list(auto_bi[\"Reported Claims\"].origin.year)\n", + "exhibit_i_s1 = pd.DataFrame(index=years)\n", + "exhibit_i_s1[\"Reported (2)\"] = as_series(\n", + " auto_bi[\"Reported Claims\"].latest_diagonal\n", + ").values\n", + "exhibit_i_s1[\"Paid (3)\"] = as_series(auto_bi[\"Paid Claims\"].latest_diagonal).values\n", + "exhibit_i_s1[\"CDF Reported (4)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(reported_bi))[\"CDF\"]\n", + ").values\n", + "exhibit_i_s1[\"CDF Paid (5)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(paid_bi))[\"CDF\"]\n", + ").values\n", + "exhibit_i_s1[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", + "exhibit_i_s1[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", + "exhibit_i_s1[\"Initial Selected (8)\"] = as_series(initial_selected).values\n", + "exhibit_i_s1[\"Earned Premium (9)\"] = as_series(\n", + " auto_bi[\"Earned Premium\"].latest_diagonal\n", + ").values\n", + "exhibit_i_s1[\"Trend to 7/1/08 (10)\"] = as_series(trend_factors).values\n", + "exhibit_i_s1[\"Tort Reform (11)\"] = tort_factors\n", + "exhibit_i_s1[\"Trended Adj Ult (12)\"] = as_series(trended_adj).values\n", + "exhibit_i_s1[\"Trended Adj Claim Ratio (13)\"] = as_series(claim_ratio).values\n", + "display(exhibit_i_s1)\n", + "\n", + "ratios = as_series(claim_ratio)\n", + "avg_00_05 = float(np.round(ratios.loc[2000:2005].mean(), 3))\n", + "avg_00_05_xhl = float(np.round(avg_ex_high_low(ratios.loc[2000:2005]), 3))\n", + "avg_01_06 = float(np.round(ratios.loc[2001:2006].mean(), 3))\n", + "avg_01_06_xhl = float(np.round(avg_ex_high_low(ratios.loc[2001:2006]), 3))\n", + "selected_claim_ratio = 0.80\n", + "el_reported = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(\n", + " auto_bi[\"Reported Claims\"], sample_weight=auto_bi[\"Earned Premium\"].latest_diagonal\n", + ")\n", + "el_paid = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(\n", + " auto_bi[\"Paid Claims\"], sample_weight=auto_bi[\"Earned Premium\"].latest_diagonal\n", + ")\n", + "expected_2008 = float(el_reported.ultimate_.loc[:, :, \"2008\", :].sum())\n", + "unpaid_2008 = float(el_paid.ibnr_.loc[:, :, \"2008\", :].sum())\n", + "ibnr_2008 = float(el_reported.ibnr_.loc[:, :, \"2008\", :].sum())\n", + "\n", + "exhibit_i_s1_summary = pd.Series(\n", + " {\n", + " \"Avg 2000-2005 (14)\": avg_00_05,\n", + " \"Avg 2000-2005 ex Hi/Lo (14)\": avg_00_05_xhl,\n", + " \"Avg 2001-2006 (14)\": avg_01_06,\n", + " \"Avg 2001-2006 ex Hi/Lo (14)\": avg_01_06_xhl,\n", + " \"Selected Claim Ratio (15)\": selected_claim_ratio,\n", + " \"Expected Claims 2008 (16)\": expected_2008,\n", + " \"Total Unpaid 2008 (17)\": unpaid_2008,\n", + " \"IBNR 2008 (17)\": ibnr_2008,\n", + " },\n", + " name=\"Items (14)-(17)\",\n", + ")\n", + "display(\n", + " exhibit_i_s1_summary.map(\n", + " lambda x: f\"{x:,.0f}\" if abs(x) >= 1 else f\"{x:.3f}\"\n", + " ).to_frame()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "8986d8ac", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.455019Z", + "iopub.status.busy": "2026-07-23T16:32:57.454848Z", + "iopub.status.idle": "2026-07-23T16:32:57.463138Z", + "shell.execute_reply": "2026-07-23T16:32:57.462735Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit I Sheet 1 — reconcile to Friedland PDF p140\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Reported (2)\"],\n", + " [\n", + " 10000000,\n", + " 8000000,\n", + " 9400000,\n", + " 15600000,\n", + " 16500000,\n", + " 18500000,\n", + " 16500000,\n", + " 14000000,\n", + " 8700000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Paid (3)\"],\n", + " [9500000, 7200000, 7600000, 7800000, 11200000, 10200000, 6000000, 3000000, 750000],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Ult Reported (6)\"],\n", + " [\n", + " 10050000,\n", + " 8160000,\n", + " 9682000,\n", + " 17160000,\n", + " 19800000,\n", + " 25900000,\n", + " 29700000,\n", + " 40600000,\n", + " 34800000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Ult Paid (7)\"],\n", + " [\n", + " 9975000,\n", + " 8280000,\n", + " 9500000,\n", + " 10530000,\n", + " 19600000,\n", + " 25500000,\n", + " 30000000,\n", + " 45000000,\n", + " 67500000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Initial Selected (8)\"],\n", + " [\n", + " 10012500,\n", + " 8220000,\n", + " 9591000,\n", + " 13845000,\n", + " 19700000,\n", + " 25700000,\n", + " 29850000,\n", + " 42800000,\n", + " 51150000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Earned Premium (9)\"],\n", + " [\n", + " 24000000,\n", + " 18000000,\n", + " 19000000,\n", + " 23000000,\n", + " 32000000,\n", + " 47000000,\n", + " 50000000,\n", + " 57000000,\n", + " 62000000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Trend to 7/1/08 (10)\"],\n", + " [2.954, 2.58, 2.253, 1.968, 1.719, 1.501, 1.311, 1.145, 1],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Trended Adj Ult (12)\"],\n", + " [\n", + " 19816540,\n", + " 14209092,\n", + " 14477710,\n", + " 18255463,\n", + " 25398225,\n", + " 38575700,\n", + " 39133350,\n", + " 49006000,\n", + " 51150000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s1[\"Trended Adj Claim Ratio (13)\"],\n", + " [0.83, 0.79, 0.76, 0.79, 0.79, 0.82, 0.78, 0.86, 0.82],\n", + ")\n", + "assert np.isclose(avg_00_05, 0.797)\n", + "assert np.isclose(avg_00_05_xhl, 0.798)\n", + "assert np.isclose(avg_01_06, 0.788)\n", + "assert np.isclose(avg_01_06_xhl, 0.788, atol=0.001)\n", + "assert np.isclose(expected_2008, 49600000)\n", + "assert np.isclose(unpaid_2008, 48850000)\n", + "assert np.isclose(ibnr_2008, 40900000)" + ] + }, + { + "cell_type": "markdown", + "id": "ec20e605", + "metadata": {}, + "source": [ + "## P141 (Exhibit I Sheet 2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "77e137a8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.465440Z", + "iopub.status.busy": "2026-07-23T16:32:57.465239Z", + "iopub.status.idle": "2026-07-23T16:32:57.693257Z", + "shell.execute_reply": "2026-07-23T16:32:57.692892Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "16398dc9", - "metadata": {}, - "source": [ - "## P140 (Exhibit I Sheet 1)\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", + "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Population (9)Trend to 7/1/08 (10)Trended Ult (11)Trended Pure Premium (12)
1998900000.0890000.01.0151.046913500.0930940.0922220.0709000.02.0611900695.02.68
19991200000.01170000.01.0201.0671224000.01248390.01236195.0724000.01.9172369786.03.27
20001300000.01265000.01.0301.1091339000.01402885.01370942.5736000.01.7832444390.03.32
20011800000.01600000.01.0511.1871891800.01899200.01895500.0740000.01.6593144634.04.25
20021450000.01200000.01.0771.3061561650.01567200.01564425.0750000.01.5432413908.03.22
20031400000.01050000.01.1311.4891583400.01563450.01573425.0760000.01.4362259438.02.97
20042400000.0900000.01.2441.7492985600.01574100.02279850.0770000.01.3353043600.03.95
20051800000.0860000.01.3942.2742509200.01955640.02232420.0775000.01.2422772666.03.58
20061500000.0525000.01.6163.1832424000.01671075.02047537.5780000.01.1562366953.03.03
20071200000.0750000.01.9405.0932328000.03819750.03073875.0785000.01.0753304416.04.21
2008600000.0170000.03.10420.3731862400.03463410.02662905.0790000.01.0002662905.03.37
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Population (9) Trend to 7/1/08 (10) Trended Ult (11) Trended Pure Premium (12)\n", + "1998 900000.0 890000.0 1.015 1.046 913500.0 930940.0 922220.0 709000.0 2.061 1900695.0 2.68\n", + "1999 1200000.0 1170000.0 1.020 1.067 1224000.0 1248390.0 1236195.0 724000.0 1.917 2369786.0 3.27\n", + "2000 1300000.0 1265000.0 1.030 1.109 1339000.0 1402885.0 1370942.5 736000.0 1.783 2444390.0 3.32\n", + "2001 1800000.0 1600000.0 1.051 1.187 1891800.0 1899200.0 1895500.0 740000.0 1.659 3144634.0 4.25\n", + "2002 1450000.0 1200000.0 1.077 1.306 1561650.0 1567200.0 1564425.0 750000.0 1.543 2413908.0 3.22\n", + "2003 1400000.0 1050000.0 1.131 1.489 1583400.0 1563450.0 1573425.0 760000.0 1.436 2259438.0 2.97\n", + "2004 2400000.0 900000.0 1.244 1.749 2985600.0 1574100.0 2279850.0 770000.0 1.335 3043600.0 3.95\n", + "2005 1800000.0 860000.0 1.394 2.274 2509200.0 1955640.0 2232420.0 775000.0 1.242 2772666.0 3.58\n", + "2006 1500000.0 525000.0 1.616 3.183 2424000.0 1671075.0 2047537.5 780000.0 1.156 2366953.0 3.03\n", + "2007 1200000.0 750000.0 1.940 5.093 2328000.0 3819750.0 3073875.0 785000.0 1.075 3304416.0 4.21\n", + "2008 600000.0 170000.0 3.104 20.373 1862400.0 3463410.0 2662905.0 790000.0 1.000 2662905.0 3.37" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 2, - "id": "2618380c", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.262396Z", - "iopub.status.busy": "2026-07-23T16:32:57.261697Z", - "iopub.status.idle": "2026-07-23T16:32:57.450905Z", - "shell.execute_reply": "2026-07-23T16:32:57.450503Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Trend to 7/1/08 (10)Tort Reform (11)Trended Adj Ult (12)Trended Adj Claim Ratio (13)
200010000000.09500000.01.0051.0510050000.09975000.010012500.024000000.02.9540.6719816540.00.83
20018000000.07200000.01.0201.158160000.08280000.08220000.018000000.02.5800.6714209092.00.79
20029400000.07600000.01.0301.259682000.09500000.09591000.019000000.02.2530.6714477710.00.76
200315600000.07800000.01.1001.3517160000.010530000.013845000.023000000.01.9680.6718255463.00.79
200416500000.011200000.01.2001.7519800000.019600000.019700000.032000000.01.7190.7525398225.00.79
200518500000.010200000.01.4002.5025900000.025500000.025700000.047000000.01.5011.0038575700.00.82
200616500000.06000000.01.8005.0029700000.030000000.029850000.050000000.01.3111.0039133350.00.78
200714000000.03000000.02.90015.0040600000.045000000.042800000.057000000.01.1451.0049006000.00.86
20088700000.0750000.04.00090.0034800000.067500000.051150000.062000000.01.0001.0051150000.00.82
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Trend to 7/1/08 (10) Tort Reform (11) Trended Adj Ult (12) Trended Adj Claim Ratio (13)\n", - "2000 10000000.0 9500000.0 1.005 1.05 10050000.0 9975000.0 10012500.0 24000000.0 2.954 0.67 19816540.0 0.83\n", - "2001 8000000.0 7200000.0 1.020 1.15 8160000.0 8280000.0 8220000.0 18000000.0 2.580 0.67 14209092.0 0.79\n", - "2002 9400000.0 7600000.0 1.030 1.25 9682000.0 9500000.0 9591000.0 19000000.0 2.253 0.67 14477710.0 0.76\n", - "2003 15600000.0 7800000.0 1.100 1.35 17160000.0 10530000.0 13845000.0 23000000.0 1.968 0.67 18255463.0 0.79\n", - "2004 16500000.0 11200000.0 1.200 1.75 19800000.0 19600000.0 19700000.0 32000000.0 1.719 0.75 25398225.0 0.79\n", - "2005 18500000.0 10200000.0 1.400 2.50 25900000.0 25500000.0 25700000.0 47000000.0 1.501 1.00 38575700.0 0.82\n", - "2006 16500000.0 6000000.0 1.800 5.00 29700000.0 30000000.0 29850000.0 50000000.0 1.311 1.00 39133350.0 0.78\n", - "2007 14000000.0 3000000.0 2.900 15.00 40600000.0 45000000.0 42800000.0 57000000.0 1.145 1.00 49006000.0 0.86\n", - "2008 8700000.0 750000.0 4.000 90.00 34800000.0 67500000.0 51150000.0 62000000.0 1.000 1.00 51150000.0 0.82" - ] - }, - "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", - "
Items (14)-(17)
Avg 2000-2005 (14)0.797
Avg 2000-2005 ex Hi/Lo (14)0.798
Avg 2001-2006 (14)0.788
Avg 2001-2006 ex Hi/Lo (14)0.787
Selected Claim Ratio (15)0.800
Expected Claims 2008 (16)49,600,000
Total Unpaid 2008 (17)48,850,000
IBNR 2008 (17)40,900,000
\n", - "
" - ], - "text/plain": [ - " Items (14)-(17)\n", - "Avg 2000-2005 (14) 0.797\n", - "Avg 2000-2005 ex Hi/Lo (14) 0.798\n", - "Avg 2001-2006 (14) 0.788\n", - "Avg 2001-2006 ex Hi/Lo (14) 0.787\n", - "Selected Claim Ratio (15) 0.800\n", - "Expected Claims 2008 (16) 49,600,000\n", - "Total Unpaid 2008 (17) 48,850,000\n", - "IBNR 2008 (17) 40,900,000" - ] - }, - "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", + "
Items (13)-(16)
Avg 2000-2005 (13)3.55
Avg 2000-2005 ex Hi/Lo (13)3.52
Avg 2001-2006 (13)3.50
Avg 2001-2006 ex Hi/Lo (13)3.44
Selected Pure Premium (14)3.50
Expected Claims 2008 (15)2,765,000
Total Unpaid 2008 (16)2,595,000
IBNR 2008 (16)2,165,000
\n", + "
" ], - "source": [ - "auto_bi = cl.load_sample(\"friedland_auto_bi_insurer\")\n", - "\n", - "reported_pattern = {\n", - " 12: 4, 24: 2.9, 36: 1.8, 48: 1.4, 60: 1.2, 72: 1.1, 84: 1.03, 96: 1.02, 108: 1.005,\n", - "}\n", - "paid_pattern = {\n", - " 12: 90, 24: 15, 36: 5, 48: 2.5, 60: 1.75, 72: 1.35, 84: 1.25, 96: 1.15, 108: 1.05,\n", - "}\n", - "\n", - "reported_bi = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", - " auto_bi[\"Reported Claims\"]\n", - ")\n", - "paid_bi = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", - " auto_bi[\"Paid Claims\"]\n", - ")\n", - "reported_ultimate = cl.Chainladder().fit(reported_bi).ultimate_\n", - "paid_ultimate = cl.Chainladder().fit(paid_bi).ultimate_\n", - "initial_selected = (reported_ultimate + paid_ultimate) / 2\n", - "\n", - "trend_factors = np.round(\n", - " cl.Trend(trends=[0.145], dates=[(\"2008-12-31\", \"2000-01-01\")])\n", - " .fit(auto_bi[\"Earned Premium\"])\n", - " .trend_.latest_diagonal,\n", - " 3,\n", - ")\n", - "tort_factors = np.array([0.670, 0.670, 0.670, 0.670, 0.750, 1.0, 1.0, 1.0, 1.0])\n", - "trended_adj = np.round(\n", - " trend_factors * initial_selected * tort_factors.reshape(1, 1, -1, 1), 0\n", - ")\n", - "claim_ratio = np.round(trended_adj / auto_bi[\"Earned Premium\"].latest_diagonal, 2)\n", - "\n", - "years = list(auto_bi[\"Reported Claims\"].origin.year)\n", - "exhibit_i_s1 = pd.DataFrame(index=years)\n", - "exhibit_i_s1[\"Reported (2)\"] = as_series(auto_bi[\"Reported Claims\"].latest_diagonal).values\n", - "exhibit_i_s1[\"Paid (3)\"] = as_series(auto_bi[\"Paid Claims\"].latest_diagonal).values\n", - "exhibit_i_s1[\"CDF Reported (4)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(reported_bi))[\"CDF\"]\n", - ").values\n", - "exhibit_i_s1[\"CDF Paid (5)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(paid_bi))[\"CDF\"]\n", - ").values\n", - "exhibit_i_s1[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", - "exhibit_i_s1[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", - "exhibit_i_s1[\"Initial Selected (8)\"] = as_series(initial_selected).values\n", - "exhibit_i_s1[\"Earned Premium (9)\"] = as_series(auto_bi[\"Earned Premium\"].latest_diagonal).values\n", - "exhibit_i_s1[\"Trend to 7/1/08 (10)\"] = as_series(trend_factors).values\n", - "exhibit_i_s1[\"Tort Reform (11)\"] = tort_factors\n", - "exhibit_i_s1[\"Trended Adj Ult (12)\"] = as_series(trended_adj).values\n", - "exhibit_i_s1[\"Trended Adj Claim Ratio (13)\"] = as_series(claim_ratio).values\n", - "display(exhibit_i_s1)\n", - "\n", - "ratios = as_series(claim_ratio)\n", - "avg_00_05 = float(np.round(ratios.loc[2000:2005].mean(), 3))\n", - "avg_00_05_xhl = float(np.round(avg_ex_high_low(ratios.loc[2000:2005]), 3))\n", - "avg_01_06 = float(np.round(ratios.loc[2001:2006].mean(), 3))\n", - "avg_01_06_xhl = float(np.round(avg_ex_high_low(ratios.loc[2001:2006]), 3))\n", - "selected_claim_ratio = 0.80\n", - "el_reported = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(\n", - " auto_bi[\"Reported Claims\"], sample_weight=auto_bi[\"Earned Premium\"].latest_diagonal\n", - ")\n", - "el_paid = cl.ExpectedLoss(apriori=selected_claim_ratio).fit(\n", - " auto_bi[\"Paid Claims\"], sample_weight=auto_bi[\"Earned Premium\"].latest_diagonal\n", - ")\n", - "expected_2008 = float(el_reported.ultimate_.loc[:, :, \"2008\", :].sum())\n", - "unpaid_2008 = float(el_paid.ibnr_.loc[:, :, \"2008\", :].sum())\n", - "ibnr_2008 = float(el_reported.ibnr_.loc[:, :, \"2008\", :].sum())\n", - "\n", - "exhibit_i_s1_summary = pd.Series(\n", - " {\n", - " \"Avg 2000-2005 (14)\": avg_00_05,\n", - " \"Avg 2000-2005 ex Hi/Lo (14)\": avg_00_05_xhl,\n", - " \"Avg 2001-2006 (14)\": avg_01_06,\n", - " \"Avg 2001-2006 ex Hi/Lo (14)\": avg_01_06_xhl,\n", - " \"Selected Claim Ratio (15)\": selected_claim_ratio,\n", - " \"Expected Claims 2008 (16)\": expected_2008,\n", - " \"Total Unpaid 2008 (17)\": unpaid_2008,\n", - " \"IBNR 2008 (17)\": ibnr_2008,\n", - " },\n", - " name=\"Items (14)-(17)\",\n", - ")\n", - "display(\n", - " exhibit_i_s1_summary.map(\n", - " lambda x: f\"{x:,.0f}\" if abs(x) >= 1 else f\"{x:.3f}\"\n", - " ).to_frame()\n", - ")\n" + "text/plain": [ + " Items (13)-(16)\n", + "Avg 2000-2005 (13) 3.55\n", + "Avg 2000-2005 ex Hi/Lo (13) 3.52\n", + "Avg 2001-2006 (13) 3.50\n", + "Avg 2001-2006 ex Hi/Lo (13) 3.44\n", + "Selected Pure Premium (14) 3.50\n", + "Expected Claims 2008 (15) 2,765,000\n", + "Total Unpaid 2008 (16) 2,595,000\n", + "IBNR 2008 (16) 2,165,000" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "gl = cl.load_sample(\"friedland_gl_self_insurer\")\n", + "\n", + "reported_pattern = {\n", + " 12: 3.104,\n", + " 24: 1.940,\n", + " 36: 1.616,\n", + " 48: 1.394,\n", + " 60: 1.244,\n", + " 72: 1.131,\n", + " 84: 1.077,\n", + " 96: 1.051,\n", + " 108: 1.030,\n", + " 120: 1.020,\n", + " 132: 1.015,\n", + "}\n", + "paid_pattern = {\n", + " 12: 20.373,\n", + " 24: 5.093,\n", + " 36: 3.183,\n", + " 48: 2.274,\n", + " 60: 1.749,\n", + " 72: 1.489,\n", + " 84: 1.306,\n", + " 96: 1.187,\n", + " 108: 1.109,\n", + " 120: 1.067,\n", + " 132: 1.046,\n", + "}\n", + "\n", + "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", + " gl[\"Reported Claims\"]\n", + ")\n", + "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", + " gl[\"Paid Claims\"]\n", + ")\n", + "reported_ultimate = cl.Chainladder().fit(reported).ultimate_\n", + "paid_ultimate = cl.Chainladder().fit(paid).ultimate_\n", + "selected_ultimate = (reported_ultimate + paid_ultimate) / 2\n", + "population = gl[\"Population\"].latest_diagonal\n", + "trend_factors = np.round(\n", + " cl\n", + " .Trend(trends=[0.075], dates=[(\"2008-12-31\", \"1998-01-01\")])\n", + " .fit(gl[\"Population\"])\n", + " .trend_.latest_diagonal,\n", + " 3,\n", + ")\n", + "trended_ult = np.round(selected_ultimate * trend_factors, 0)\n", + "pure_premium = np.round(trended_ult / population, 2)\n", + "\n", + "years = list(gl[\"Reported Claims\"].origin.year)\n", + "exhibit_i_s2 = pd.DataFrame(index=years)\n", + "exhibit_i_s2[\"Reported (2)\"] = as_series(gl[\"Reported Claims\"].latest_diagonal).values\n", + "exhibit_i_s2[\"Paid (3)\"] = as_series(gl[\"Paid Claims\"].latest_diagonal).values\n", + "exhibit_i_s2[\"CDF Reported (4)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", + ").values\n", + "exhibit_i_s2[\"CDF Paid (5)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", + ").values\n", + "exhibit_i_s2[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", + "exhibit_i_s2[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", + "exhibit_i_s2[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", + "exhibit_i_s2[\"Population (9)\"] = as_series(population).values\n", + "exhibit_i_s2[\"Trend to 7/1/08 (10)\"] = as_series(trend_factors).values\n", + "exhibit_i_s2[\"Trended Ult (11)\"] = as_series(trended_ult).values\n", + "exhibit_i_s2[\"Trended Pure Premium (12)\"] = as_series(pure_premium).values\n", + "display(exhibit_i_s2)\n", + "\n", + "pp = as_series(pure_premium)\n", + "avg_00_05 = float(np.round(pp.loc[2000:2005].mean(), 2))\n", + "avg_00_05_xhl = float(np.round(avg_ex_high_low(pp.loc[2000:2005]), 2))\n", + "avg_01_06 = float(np.round(pp.loc[2001:2006].mean(), 2))\n", + "avg_01_06_xhl = float(np.round(avg_ex_high_low(pp.loc[2001:2006]), 2))\n", + "selected_pure_premium = 3.50\n", + "el_reported = cl.ExpectedLoss(apriori=selected_pure_premium).fit(\n", + " reported, sample_weight=population\n", + ")\n", + "el_paid = cl.ExpectedLoss(apriori=selected_pure_premium).fit(\n", + " paid, sample_weight=population\n", + ")\n", + "expected_2008 = float(el_reported.ultimate_.loc[:, :, \"2008\", :].sum())\n", + "unpaid_2008 = float(el_paid.ibnr_.loc[:, :, \"2008\", :].sum())\n", + "ibnr_2008 = float(el_reported.ibnr_.loc[:, :, \"2008\", :].sum())\n", + "\n", + "exhibit_i_s2_summary = pd.Series(\n", + " {\n", + " \"Avg 2000-2005 (13)\": avg_00_05,\n", + " \"Avg 2000-2005 ex Hi/Lo (13)\": avg_00_05_xhl,\n", + " \"Avg 2001-2006 (13)\": avg_01_06,\n", + " \"Avg 2001-2006 ex Hi/Lo (13)\": avg_01_06_xhl,\n", + " \"Selected Pure Premium (14)\": selected_pure_premium,\n", + " \"Expected Claims 2008 (15)\": expected_2008,\n", + " \"Total Unpaid 2008 (16)\": unpaid_2008,\n", + " \"IBNR 2008 (16)\": ibnr_2008,\n", + " },\n", + " name=\"Items (13)-(16)\",\n", + ")\n", + "display(\n", + " exhibit_i_s2_summary.map(\n", + " lambda x: f\"{x:,.0f}\" if abs(x) >= 100 else f\"{x:.2f}\"\n", + " ).to_frame()\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "4457a327", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.696381Z", + "iopub.status.busy": "2026-07-23T16:32:57.696194Z", + "iopub.status.idle": "2026-07-23T16:32:57.703635Z", + "shell.execute_reply": "2026-07-23T16:32:57.702954Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit I Sheet 2 — reconcile to Friedland PDF p141\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Reported (2)\"],\n", + " [\n", + " 900000,\n", + " 1200000,\n", + " 1300000,\n", + " 1800000,\n", + " 1450000,\n", + " 1400000,\n", + " 2400000,\n", + " 1800000,\n", + " 1500000,\n", + " 1200000,\n", + " 600000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Paid (3)\"],\n", + " [\n", + " 890000,\n", + " 1170000,\n", + " 1265000,\n", + " 1600000,\n", + " 1200000,\n", + " 1050000,\n", + " 900000,\n", + " 860000,\n", + " 525000,\n", + " 750000,\n", + " 170000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Ult Reported (6)\"],\n", + " [\n", + " 913500,\n", + " 1224000,\n", + " 1339000,\n", + " 1891800,\n", + " 1561650,\n", + " 1583400,\n", + " 2985600,\n", + " 2509200,\n", + " 2424000,\n", + " 2328000,\n", + " 1862400,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Ult Paid (7)\"],\n", + " [\n", + " 930940,\n", + " 1248390,\n", + " 1402885,\n", + " 1899200,\n", + " 1567200,\n", + " 1563450,\n", + " 1574100,\n", + " 1955640,\n", + " 1671075,\n", + " 3819750,\n", + " 3463410,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Population (9)\"],\n", + " [\n", + " 709000,\n", + " 724000,\n", + " 736000,\n", + " 740000,\n", + " 750000,\n", + " 760000,\n", + " 770000,\n", + " 775000,\n", + " 780000,\n", + " 785000,\n", + " 790000,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Trend to 7/1/08 (10)\"],\n", + " [2.061, 1.917, 1.783, 1.659, 1.543, 1.436, 1.335, 1.242, 1.156, 1.075, 1],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Trended Ult (11)\"],\n", + " [\n", + " 1900695,\n", + " 2369786,\n", + " 2444390,\n", + " 3144635,\n", + " 2413908,\n", + " 2259438,\n", + " 3043600,\n", + " 2772666,\n", + " 2366953,\n", + " 3304416,\n", + " 2662905,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_i_s2[\"Trended Pure Premium (12)\"],\n", + " [2.68, 3.27, 3.32, 4.25, 3.22, 2.97, 3.95, 3.58, 3.03, 4.21, 3.37],\n", + " atol=0.01,\n", + ")\n", + "assert np.isclose(avg_00_05, 3.55)\n", + "assert np.isclose(avg_00_05_xhl, 3.52)\n", + "assert np.isclose(avg_01_06, 3.50)\n", + "assert np.isclose(avg_01_06_xhl, 3.45, atol=0.011) # PDF 3.45; 3.445 rounds to 3.44\n", + "assert np.isclose(expected_2008, 2765000)\n", + "assert np.isclose(unpaid_2008, 2595000)\n", + "assert np.isclose(ibnr_2008, 2165000)" + ] + }, + { + "cell_type": "markdown", + "id": "192b4b94", + "metadata": {}, + "source": [ + "## P142 (Exhibit II Sheet 1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "37cef2cb", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.706987Z", + "iopub.status.busy": "2026-07-23T16:32:57.706774Z", + "iopub.status.idle": "2026-07-23T16:32:57.818404Z", + "shell.execute_reply": "2026-07-23T16:32:57.817888Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 3, - "id": "8986d8ac", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.455019Z", - "iopub.status.busy": "2026-07-23T16:32:57.454848Z", - "iopub.status.idle": "2026-07-23T16:32:57.463138Z", - "shell.execute_reply": "2026-07-23T16:32:57.462735Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit I Sheet 1 — reconcile to Friedland PDF p140\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Reported (2)\"],\n", - " [10000000, 8000000, 9400000, 15600000, 16500000, 18500000, 16500000, 14000000, 8700000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Paid (3)\"],\n", - " [9500000, 7200000, 7600000, 7800000, 11200000, 10200000, 6000000, 3000000, 750000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Ult Reported (6)\"],\n", - " [10050000, 8160000, 9682000, 17160000, 19800000, 25900000, 29700000, 40600000, 34800000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Ult Paid (7)\"],\n", - " [9975000, 8280000, 9500000, 10530000, 19600000, 25500000, 30000000, 45000000, 67500000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Initial Selected (8)\"],\n", - " [10012500, 8220000, 9591000, 13845000, 19700000, 25700000, 29850000, 42800000, 51150000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Earned Premium (9)\"],\n", - " [24000000, 18000000, 19000000, 23000000, 32000000, 47000000, 50000000, 57000000, 62000000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Trend to 7/1/08 (10)\"],\n", - " [2.954, 2.58, 2.253, 1.968, 1.719, 1.501, 1.311, 1.145, 1],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Trended Adj Ult (12)\"],\n", - " [19816540, 14209092, 14477710, 18255463, 25398225, 38575700, 39133350, 49006000, 51150000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s1[\"Trended Adj Claim Ratio (13)\"],\n", - " [0.83, 0.79, 0.76, 0.79, 0.79, 0.82, 0.78, 0.86, 0.82],\n", - ")\n", - "assert np.isclose(avg_00_05, 0.797)\n", - "assert np.isclose(avg_00_05_xhl, 0.798)\n", - "assert np.isclose(avg_01_06, 0.788)\n", - "assert np.isclose(avg_01_06_xhl, 0.788, atol=0.001)\n", - "assert np.isclose(expected_2008, 49600000)\n", - "assert np.isclose(unpaid_2008, 48850000)\n", - "assert np.isclose(ibnr_2008, 40900000)\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", + "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Estimated Claim Ratio (10)Selected Claim Ratio (11)Expected Claims (12)
199847742304.047644187.01.0001.00247742304.047739475.047740890.068574209.00.6960.7551430657.0
199951185767.051000534.01.0001.00451185767.051204536.051195152.068544981.00.7470.7551408736.0
200054837929.054533225.01.0011.00654892767.054860424.054876596.068907977.00.7960.7551680983.0
200156299562.055878421.01.0031.01156468461.056493084.056480772.072544955.00.7790.7554408716.0
200258592712.057807215.01.0061.02058944268.058963359.058953814.079228887.00.7440.7559421665.0
200357565344.055930654.01.0111.04058198563.058167880.058183221.086643542.00.6720.6556318302.0
200456976657.053774672.01.0231.08558287120.058345519.058316320.091763523.00.6360.6559646290.0
200556786410.050644994.01.0511.18459682517.059963673.059823095.094115312.00.6360.6561174953.0
200654641339.043606497.01.1101.40460651886.061223522.060937704.095272279.00.6400.6561926981.0
200748853563.027229969.01.2922.39063118803.065079626.064099215.095176240.00.6730.6561864556.0
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Estimated Claim Ratio (10) Selected Claim Ratio (11) Expected Claims (12)\n", + "1998 47742304.0 47644187.0 1.000 1.002 47742304.0 47739475.0 47740890.0 68574209.0 0.696 0.75 51430657.0\n", + "1999 51185767.0 51000534.0 1.000 1.004 51185767.0 51204536.0 51195152.0 68544981.0 0.747 0.75 51408736.0\n", + "2000 54837929.0 54533225.0 1.001 1.006 54892767.0 54860424.0 54876596.0 68907977.0 0.796 0.75 51680983.0\n", + "2001 56299562.0 55878421.0 1.003 1.011 56468461.0 56493084.0 56480772.0 72544955.0 0.779 0.75 54408716.0\n", + "2002 58592712.0 57807215.0 1.006 1.020 58944268.0 58963359.0 58953814.0 79228887.0 0.744 0.75 59421665.0\n", + "2003 57565344.0 55930654.0 1.011 1.040 58198563.0 58167880.0 58183221.0 86643542.0 0.672 0.65 56318302.0\n", + "2004 56976657.0 53774672.0 1.023 1.085 58287120.0 58345519.0 58316320.0 91763523.0 0.636 0.65 59646290.0\n", + "2005 56786410.0 50644994.0 1.051 1.184 59682517.0 59963673.0 59823095.0 94115312.0 0.636 0.65 61174953.0\n", + "2006 54641339.0 43606497.0 1.110 1.404 60651886.0 61223522.0 60937704.0 95272279.0 0.640 0.65 61926981.0\n", + "2007 48853563.0 27229969.0 1.292 2.390 63118803.0 65079626.0 64099215.0 95176240.0 0.673 0.65 61864556.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "ia = cl.load_sample(\"friedland_us_industry_auto\")\n", + "\n", + "reported_pattern = {\n", + " 12: 1.292,\n", + " 24: 1.110,\n", + " 36: 1.051,\n", + " 48: 1.023,\n", + " 60: 1.011,\n", + " 72: 1.006,\n", + " 84: 1.003,\n", + " 96: 1.001,\n", + " 108: 1.000,\n", + " 120: 1.000,\n", + "}\n", + "paid_pattern = {\n", + " 12: 2.390,\n", + " 24: 1.404,\n", + " 36: 1.184,\n", + " 48: 1.085,\n", + " 60: 1.040,\n", + " 72: 1.020,\n", + " 84: 1.011,\n", + " 96: 1.006,\n", + " 108: 1.004,\n", + " 120: 1.002,\n", + "}\n", + "\n", + "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", + " ia[\"Reported Claims\"]\n", + ")\n", + "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", + " ia[\"Paid Claims\"]\n", + ")\n", + "reported_ultimate = cl.Chainladder().fit(reported).ultimate_\n", + "paid_ultimate = cl.Chainladder().fit(paid).ultimate_\n", + "selected_ultimate = np.round((reported_ultimate + paid_ultimate) / 2, 0)\n", + "earned_premium = ia[\"Earned Premium\"].latest_diagonal\n", + "estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)\n", + "selected_claim_ratio = np.array([\n", + " 0.75,\n", + " 0.75,\n", + " 0.75,\n", + " 0.75,\n", + " 0.75,\n", + " 0.65,\n", + " 0.65,\n", + " 0.65,\n", + " 0.65,\n", + " 0.65,\n", + "])\n", + "sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)\n", + "el_reported = cl.ExpectedLoss(apriori=1).fit(\n", + " ia[\"Reported Claims\"], sample_weight=sample_weight\n", + ")\n", + "el_paid = cl.ExpectedLoss(apriori=1).fit(ia[\"Paid Claims\"], sample_weight=sample_weight)\n", + "expected_claims = np.round(el_reported.ultimate_, 0)\n", + "\n", + "years = list(ia[\"Reported Claims\"].origin.year)\n", + "exhibit_ii_s1 = pd.DataFrame(index=years)\n", + "exhibit_ii_s1[\"Reported (2)\"] = as_series(ia[\"Reported Claims\"].latest_diagonal).values\n", + "exhibit_ii_s1[\"Paid (3)\"] = as_series(ia[\"Paid Claims\"].latest_diagonal).values\n", + "exhibit_ii_s1[\"CDF Reported (4)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", + ").values\n", + "exhibit_ii_s1[\"CDF Paid (5)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", + ").values\n", + "exhibit_ii_s1[\"Ult Reported (6)\"] = as_series(np.round(reported_ultimate, 0)).values\n", + "exhibit_ii_s1[\"Ult Paid (7)\"] = as_series(np.round(paid_ultimate, 0)).values\n", + "exhibit_ii_s1[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", + "exhibit_ii_s1[\"Earned Premium (9)\"] = as_series(earned_premium).values\n", + "exhibit_ii_s1[\"Estimated Claim Ratio (10)\"] = as_series(estimated_claim_ratios).values\n", + "exhibit_ii_s1[\"Selected Claim Ratio (11)\"] = selected_claim_ratio\n", + "exhibit_ii_s1[\"Expected Claims (12)\"] = as_series(expected_claims).values\n", + "display(exhibit_ii_s1)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "a62265b9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.821111Z", + "iopub.status.busy": "2026-07-23T16:32:57.820985Z", + "iopub.status.idle": "2026-07-23T16:32:57.827979Z", + "shell.execute_reply": "2026-07-23T16:32:57.827354Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit II Sheet 1 — reconcile to Friedland PDF p142\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Reported (2)\"],\n", + " [\n", + " 47742304,\n", + " 51185767,\n", + " 54837929,\n", + " 56299562,\n", + " 58592712,\n", + " 57565344,\n", + " 56976657,\n", + " 56786410,\n", + " 54641339,\n", + " 48853563,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Paid (3)\"],\n", + " [\n", + " 47644187,\n", + " 51000534,\n", + " 54533225,\n", + " 55878421,\n", + " 57807215,\n", + " 55930654,\n", + " 53774672,\n", + " 50644994,\n", + " 43606497,\n", + " 27229969,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Ult Reported (6)\"],\n", + " [\n", + " 47742304,\n", + " 51185767,\n", + " 54892767,\n", + " 56468461,\n", + " 58944268,\n", + " 58198563,\n", + " 58287120,\n", + " 59682517,\n", + " 60651886,\n", + " 63118803,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Ult Paid (7)\"],\n", + " [\n", + " 47739475,\n", + " 51204536,\n", + " 54860424,\n", + " 56493084,\n", + " 58963359,\n", + " 58167880,\n", + " 58345519,\n", + " 59963673,\n", + " 61223522,\n", + " 65079626,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Initial Selected (8)\"],\n", + " [\n", + " 47740890,\n", + " 51195152,\n", + " 54876596,\n", + " 56480772,\n", + " 58953814,\n", + " 58183221,\n", + " 58316320,\n", + " 59823095,\n", + " 60937704,\n", + " 64099215,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Earned Premium (9)\"],\n", + " [\n", + " 68574209,\n", + " 68544981,\n", + " 68907977,\n", + " 72544955,\n", + " 79228887,\n", + " 86643542,\n", + " 91763523,\n", + " 94115312,\n", + " 95272279,\n", + " 95176240,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Estimated Claim Ratio (10)\"],\n", + " [0.696, 0.747, 0.796, 0.779, 0.744, 0.672, 0.636, 0.636, 0.640, 0.673],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s1[\"Expected Claims (12)\"],\n", + " [\n", + " 51430657,\n", + " 51408736,\n", + " 51680983,\n", + " 54408716,\n", + " 59421665,\n", + " 56318302,\n", + " 59646290,\n", + " 61174953,\n", + " 61926981,\n", + " 61864556,\n", + " ],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "c14233ed", + "metadata": {}, + "source": [ + "## P143 (Exhibit II Sheet 2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "fc3209f1", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.830544Z", + "iopub.status.busy": "2026-07-23T16:32:57.830274Z", + "iopub.status.idle": "2026-07-23T16:32:57.850125Z", + "shell.execute_reply": "2026-07-23T16:32:57.849826Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "ec20e605", - "metadata": {}, - "source": [ - "## P141 (Exhibit I Sheet 2)\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", + "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
199847742304.047644187.051430657.098117.03688353.03786470.0
199951185767.051000534.051408736.0185233.0222969.0408202.0
200054837929.054533225.051680983.0304704.0-3156946.0-2852242.0
200156299562.055878421.054408716.0421141.0-1890846.0-1469705.0
200258592712.057807215.059421665.0785497.0828953.01614450.0
200357565344.055930654.056318302.01634690.0-1247042.0387648.0
200456976657.053774672.059646290.03201985.02669633.05871618.0
200556786410.050644994.061174953.06141416.04388543.010529959.0
200654641339.043606497.061926981.011034842.07285642.018320484.0
200748853563.027229969.061864556.021623594.013010993.034634587.0
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", + "1998 47742304.0 47644187.0 51430657.0 98117.0 3688353.0 3786470.0\n", + "1999 51185767.0 51000534.0 51408736.0 185233.0 222969.0 408202.0\n", + "2000 54837929.0 54533225.0 51680983.0 304704.0 -3156946.0 -2852242.0\n", + "2001 56299562.0 55878421.0 54408716.0 421141.0 -1890846.0 -1469705.0\n", + "2002 58592712.0 57807215.0 59421665.0 785497.0 828953.0 1614450.0\n", + "2003 57565344.0 55930654.0 56318302.0 1634690.0 -1247042.0 387648.0\n", + "2004 56976657.0 53774672.0 59646290.0 3201985.0 2669633.0 5871618.0\n", + "2005 56786410.0 50644994.0 61174953.0 6141416.0 4388543.0 10529959.0\n", + "2006 54641339.0 43606497.0 61926981.0 11034842.0 7285642.0 18320484.0\n", + "2007 48853563.0 27229969.0 61864556.0 21623594.0 13010993.0 34634587.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 4, - "id": "77e137a8", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.465440Z", - "iopub.status.busy": "2026-07-23T16:32:57.465239Z", - "iopub.status.idle": "2026-07-23T16:32:57.693257Z", - "shell.execute_reply": "2026-07-23T16:32:57.692892Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Population (9)Trend to 7/1/08 (10)Trended Ult (11)Trended Pure Premium (12)
1998900000.0890000.01.0151.046913500.0930940.0922220.0709000.02.0611900695.02.68
19991200000.01170000.01.0201.0671224000.01248390.01236195.0724000.01.9172369786.03.27
20001300000.01265000.01.0301.1091339000.01402885.01370942.5736000.01.7832444390.03.32
20011800000.01600000.01.0511.1871891800.01899200.01895500.0740000.01.6593144634.04.25
20021450000.01200000.01.0771.3061561650.01567200.01564425.0750000.01.5432413908.03.22
20031400000.01050000.01.1311.4891583400.01563450.01573425.0760000.01.4362259438.02.97
20042400000.0900000.01.2441.7492985600.01574100.02279850.0770000.01.3353043600.03.95
20051800000.0860000.01.3942.2742509200.01955640.02232420.0775000.01.2422772666.03.58
20061500000.0525000.01.6163.1832424000.01671075.02047537.5780000.01.1562366953.03.03
20071200000.0750000.01.9405.0932328000.03819750.03073875.0785000.01.0753304416.04.21
2008600000.0170000.03.10420.3731862400.03463410.02662905.0790000.01.0002662905.03.37
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Population (9) Trend to 7/1/08 (10) Trended Ult (11) Trended Pure Premium (12)\n", - "1998 900000.0 890000.0 1.015 1.046 913500.0 930940.0 922220.0 709000.0 2.061 1900695.0 2.68\n", - "1999 1200000.0 1170000.0 1.020 1.067 1224000.0 1248390.0 1236195.0 724000.0 1.917 2369786.0 3.27\n", - "2000 1300000.0 1265000.0 1.030 1.109 1339000.0 1402885.0 1370942.5 736000.0 1.783 2444390.0 3.32\n", - "2001 1800000.0 1600000.0 1.051 1.187 1891800.0 1899200.0 1895500.0 740000.0 1.659 3144634.0 4.25\n", - "2002 1450000.0 1200000.0 1.077 1.306 1561650.0 1567200.0 1564425.0 750000.0 1.543 2413908.0 3.22\n", - "2003 1400000.0 1050000.0 1.131 1.489 1583400.0 1563450.0 1573425.0 760000.0 1.436 2259438.0 2.97\n", - "2004 2400000.0 900000.0 1.244 1.749 2985600.0 1574100.0 2279850.0 770000.0 1.335 3043600.0 3.95\n", - "2005 1800000.0 860000.0 1.394 2.274 2509200.0 1955640.0 2232420.0 775000.0 1.242 2772666.0 3.58\n", - "2006 1500000.0 525000.0 1.616 3.183 2424000.0 1671075.0 2047537.5 780000.0 1.156 2366953.0 3.03\n", - "2007 1200000.0 750000.0 1.940 5.093 2328000.0 3819750.0 3073875.0 785000.0 1.075 3304416.0 4.21\n", - "2008 600000.0 170000.0 3.104 20.373 1862400.0 3463410.0 2662905.0 790000.0 1.000 2662905.0 3.37" - ] - }, - "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", - "
Items (13)-(16)
Avg 2000-2005 (13)3.55
Avg 2000-2005 ex Hi/Lo (13)3.52
Avg 2001-2006 (13)3.50
Avg 2001-2006 ex Hi/Lo (13)3.44
Selected Pure Premium (14)3.50
Expected Claims 2008 (15)2,765,000
Total Unpaid 2008 (16)2,595,000
IBNR 2008 (16)2,165,000
\n", - "
" - ], - "text/plain": [ - " Items (13)-(16)\n", - "Avg 2000-2005 (13) 3.55\n", - "Avg 2000-2005 ex Hi/Lo (13) 3.52\n", - "Avg 2001-2006 (13) 3.50\n", - "Avg 2001-2006 ex Hi/Lo (13) 3.44\n", - "Selected Pure Premium (14) 3.50\n", - "Expected Claims 2008 (15) 2,765,000\n", - "Total Unpaid 2008 (16) 2,595,000\n", - "IBNR 2008 (16) 2,165,000" - ] - }, - "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", + "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
Total543481587.0498050368.0569281839.045431219.025800252.071231471.0
\n", + "
" ], - "source": [ - "gl = cl.load_sample(\"friedland_gl_self_insurer\")\n", - "\n", - "reported_pattern = {\n", - " 12: 3.104, 24: 1.940, 36: 1.616, 48: 1.394, 60: 1.244, 72: 1.131,\n", - " 84: 1.077, 96: 1.051, 108: 1.030, 120: 1.020, 132: 1.015,\n", - "}\n", - "paid_pattern = {\n", - " 12: 20.373, 24: 5.093, 36: 3.183, 48: 2.274, 60: 1.749, 72: 1.489,\n", - " 84: 1.306, 96: 1.187, 108: 1.109, 120: 1.067, 132: 1.046,\n", - "}\n", - "\n", - "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", - " gl[\"Reported Claims\"]\n", - ")\n", - "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", - " gl[\"Paid Claims\"]\n", - ")\n", - "reported_ultimate = cl.Chainladder().fit(reported).ultimate_\n", - "paid_ultimate = cl.Chainladder().fit(paid).ultimate_\n", - "selected_ultimate = (reported_ultimate + paid_ultimate) / 2\n", - "population = gl[\"Population\"].latest_diagonal\n", - "trend_factors = np.round(\n", - " cl.Trend(trends=[0.075], dates=[(\"2008-12-31\", \"1998-01-01\")])\n", - " .fit(gl[\"Population\"])\n", - " .trend_.latest_diagonal,\n", - " 3,\n", - ")\n", - "trended_ult = np.round(selected_ultimate * trend_factors, 0)\n", - "pure_premium = np.round(trended_ult / population, 2)\n", - "\n", - "years = list(gl[\"Reported Claims\"].origin.year)\n", - "exhibit_i_s2 = pd.DataFrame(index=years)\n", - "exhibit_i_s2[\"Reported (2)\"] = as_series(gl[\"Reported Claims\"].latest_diagonal).values\n", - "exhibit_i_s2[\"Paid (3)\"] = as_series(gl[\"Paid Claims\"].latest_diagonal).values\n", - "exhibit_i_s2[\"CDF Reported (4)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", - ").values\n", - "exhibit_i_s2[\"CDF Paid (5)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", - ").values\n", - "exhibit_i_s2[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", - "exhibit_i_s2[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", - "exhibit_i_s2[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", - "exhibit_i_s2[\"Population (9)\"] = as_series(population).values\n", - "exhibit_i_s2[\"Trend to 7/1/08 (10)\"] = as_series(trend_factors).values\n", - "exhibit_i_s2[\"Trended Ult (11)\"] = as_series(trended_ult).values\n", - "exhibit_i_s2[\"Trended Pure Premium (12)\"] = as_series(pure_premium).values\n", - "display(exhibit_i_s2)\n", - "\n", - "pp = as_series(pure_premium)\n", - "avg_00_05 = float(np.round(pp.loc[2000:2005].mean(), 2))\n", - "avg_00_05_xhl = float(np.round(avg_ex_high_low(pp.loc[2000:2005]), 2))\n", - "avg_01_06 = float(np.round(pp.loc[2001:2006].mean(), 2))\n", - "avg_01_06_xhl = float(np.round(avg_ex_high_low(pp.loc[2001:2006]), 2))\n", - "selected_pure_premium = 3.50\n", - "el_reported = cl.ExpectedLoss(apriori=selected_pure_premium).fit(\n", - " reported, sample_weight=population\n", - ")\n", - "el_paid = cl.ExpectedLoss(apriori=selected_pure_premium).fit(\n", - " paid, sample_weight=population\n", - ")\n", - "expected_2008 = float(el_reported.ultimate_.loc[:, :, \"2008\", :].sum())\n", - "unpaid_2008 = float(el_paid.ibnr_.loc[:, :, \"2008\", :].sum())\n", - "ibnr_2008 = float(el_reported.ibnr_.loc[:, :, \"2008\", :].sum())\n", - "\n", - "exhibit_i_s2_summary = pd.Series(\n", - " {\n", - " \"Avg 2000-2005 (13)\": avg_00_05,\n", - " \"Avg 2000-2005 ex Hi/Lo (13)\": avg_00_05_xhl,\n", - " \"Avg 2001-2006 (13)\": avg_01_06,\n", - " \"Avg 2001-2006 ex Hi/Lo (13)\": avg_01_06_xhl,\n", - " \"Selected Pure Premium (14)\": selected_pure_premium,\n", - " \"Expected Claims 2008 (15)\": expected_2008,\n", - " \"Total Unpaid 2008 (16)\": unpaid_2008,\n", - " \"IBNR 2008 (16)\": ibnr_2008,\n", - " },\n", - " name=\"Items (13)-(16)\",\n", - ")\n", - "display(\n", - " exhibit_i_s2_summary.map(\n", - " lambda x: f\"{x:,.0f}\" if abs(x) >= 100 else f\"{x:.2f}\"\n", - " ).to_frame()\n", - ")\n" + "text/plain": [ + " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", + "Total 543481587.0 498050368.0 569281839.0 45431219.0 25800252.0 71231471.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "exhibit_ii_s2 = unpaid_exhibit(\n", + " ia[\"Reported Claims\"], ia[\"Paid Claims\"], expected_claims\n", + ")\n", + "display(exhibit_ii_s2)\n", + "display(exhibit_ii_s2.sum().rename(\"Total\").to_frame().T)" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "e206f932", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.852655Z", + "iopub.status.busy": "2026-07-23T16:32:57.852317Z", + "iopub.status.idle": "2026-07-23T16:32:57.858563Z", + "shell.execute_reply": "2026-07-23T16:32:57.857022Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit II Sheet 2 — reconcile to Friedland PDF p143\n", + "assert np.allclose(\n", + " exhibit_ii_s2[\"Case Outstanding (5)\"],\n", + " [\n", + " 98117,\n", + " 185233,\n", + " 304704,\n", + " 421141,\n", + " 785497,\n", + " 1634690,\n", + " 3201985,\n", + " 6141416,\n", + " 11034842,\n", + " 21623594,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s2[\"IBNR (6)\"],\n", + " [\n", + " 3688353,\n", + " 222969,\n", + " -3156946,\n", + " -1890846,\n", + " 828953,\n", + " -1247042,\n", + " 2669633,\n", + " 4388543,\n", + " 7285642,\n", + " 13010993,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_ii_s2[\"Total Unpaid (7)\"],\n", + " [\n", + " 3786470,\n", + " 408202,\n", + " -2852242,\n", + " -1469705,\n", + " 1614450,\n", + " 387648,\n", + " 5871618,\n", + " 10529959,\n", + " 18320484,\n", + " 34634587,\n", + " ],\n", + ")\n", + "assert np.isclose(exhibit_ii_s2[\"Reported (2)\"].sum(), 543481587)\n", + "assert np.isclose(exhibit_ii_s2[\"Paid (3)\"].sum(), 498050368)\n", + "assert np.isclose(exhibit_ii_s2[\"Expected Claims (4)\"].sum(), 569281839)\n", + "assert np.isclose(exhibit_ii_s2[\"Case Outstanding (5)\"].sum(), 45431219)\n", + "assert np.isclose(exhibit_ii_s2[\"IBNR (6)\"].sum(), 25800252)\n", + "assert np.isclose(exhibit_ii_s2[\"Total Unpaid (7)\"].sum(), 71231471)" + ] + }, + { + "cell_type": "markdown", + "id": "27f7e58a", + "metadata": {}, + "source": [ + "## P144 (Exhibit III Sheet 1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "d62ffc9c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.860893Z", + "iopub.status.busy": "2026-07-23T16:32:57.860718Z", + "iopub.status.idle": "2026-07-23T16:32:57.986704Z", + "shell.execute_reply": "2026-07-23T16:32:57.985327Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 5, - "id": "4457a327", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.696381Z", - "iopub.status.busy": "2026-07-23T16:32:57.696194Z", - "iopub.status.idle": "2026-07-23T16:32:57.703635Z", - "shell.execute_reply": "2026-07-23T16:32:57.702954Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit I Sheet 2 — reconcile to Friedland PDF p141\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Reported (2)\"],\n", - " [900000, 1200000, 1300000, 1800000, 1450000, 1400000, 2400000, 1800000, 1500000, 1200000, 600000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Paid (3)\"],\n", - " [890000, 1170000, 1265000, 1600000, 1200000, 1050000, 900000, 860000, 525000, 750000, 170000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Ult Reported (6)\"],\n", - " [913500, 1224000, 1339000, 1891800, 1561650, 1583400, 2985600, 2509200, 2424000, 2328000, 1862400],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Ult Paid (7)\"],\n", - " [930940, 1248390, 1402885, 1899200, 1567200, 1563450, 1574100, 1955640, 1671075, 3819750, 3463410],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Population (9)\"],\n", - " [709000, 724000, 736000, 740000, 750000, 760000, 770000, 775000, 780000, 785000, 790000],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Trend to 7/1/08 (10)\"],\n", - " [2.061, 1.917, 1.783, 1.659, 1.543, 1.436, 1.335, 1.242, 1.156, 1.075, 1],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Trended Ult (11)\"],\n", - " [1900695, 2369786, 2444390, 3144635, 2413908, 2259438, 3043600, 2772666, 2366953, 3304416, 2662905],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_i_s2[\"Trended Pure Premium (12)\"],\n", - " [2.68, 3.27, 3.32, 4.25, 3.22, 2.97, 3.95, 3.58, 3.03, 4.21, 3.37],\n", - " atol=0.01,\n", - ")\n", - "assert np.isclose(avg_00_05, 3.55)\n", - "assert np.isclose(avg_00_05_xhl, 3.52)\n", - "assert np.isclose(avg_01_06, 3.50)\n", - "assert np.isclose(avg_01_06_xhl, 3.45, atol=0.011) # PDF 3.45; 3.445 rounds to 3.44\n", - "assert np.isclose(expected_2008, 2765000)\n", - "assert np.isclose(unpaid_2008, 2595000)\n", - "assert np.isclose(ibnr_2008, 2165000)\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", + "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Estimated Claim Ratio (10)Selected Claim Ratio (11)Expected Claims (12)
199815822.015822.01.0001.01015822.015980.015901.020000.00.7950.78315660.0
199925107.024817.00.9991.01425082.025164.025123.031500.00.7980.78324664.0
200037246.036782.00.9921.03136948.037922.037435.045000.00.8320.78335235.0
200138798.038519.00.9921.05438488.040599.039543.550000.00.7910.78339150.0
200248169.044437.01.0031.11648314.049592.048953.061183.00.8000.78347906.0
200344373.039320.01.0131.26844950.049858.047404.069175.00.6850.78354164.0
200470288.052811.01.0641.52574786.080537.077661.599322.00.7820.87186509.0
200570655.040026.01.0852.00776661.080332.078496.5138151.00.5680.783108172.0
200648804.022819.01.1963.16058370.072108.065239.0107578.00.6060.65870786.0
200731732.011865.01.5126.56947979.077941.062960.062438.01.0080.63839835.0
200818632.03409.02.55121.99947530.074995.061262.547797.01.2820.82539433.0
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Estimated Claim Ratio (10) Selected Claim Ratio (11) Expected Claims (12)\n", + "1998 15822.0 15822.0 1.000 1.010 15822.0 15980.0 15901.0 20000.0 0.795 0.783 15660.0\n", + "1999 25107.0 24817.0 0.999 1.014 25082.0 25164.0 25123.0 31500.0 0.798 0.783 24664.0\n", + "2000 37246.0 36782.0 0.992 1.031 36948.0 37922.0 37435.0 45000.0 0.832 0.783 35235.0\n", + "2001 38798.0 38519.0 0.992 1.054 38488.0 40599.0 39543.5 50000.0 0.791 0.783 39150.0\n", + "2002 48169.0 44437.0 1.003 1.116 48314.0 49592.0 48953.0 61183.0 0.800 0.783 47906.0\n", + "2003 44373.0 39320.0 1.013 1.268 44950.0 49858.0 47404.0 69175.0 0.685 0.783 54164.0\n", + "2004 70288.0 52811.0 1.064 1.525 74786.0 80537.0 77661.5 99322.0 0.782 0.871 86509.0\n", + "2005 70655.0 40026.0 1.085 2.007 76661.0 80332.0 78496.5 138151.0 0.568 0.783 108172.0\n", + "2006 48804.0 22819.0 1.196 3.160 58370.0 72108.0 65239.0 107578.0 0.606 0.658 70786.0\n", + "2007 31732.0 11865.0 1.512 6.569 47979.0 77941.0 62960.0 62438.0 1.008 0.638 39835.0\n", + "2008 18632.0 3409.0 2.551 21.999 47530.0 74995.0 61262.5 47797.0 1.282 0.825 39433.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "192b4b94", - "metadata": {}, - "source": [ - "## P142 (Exhibit II Sheet 1)\n" + "name": "stdout", + "output_type": "stream", + "text": [ + "Average estimated claim ratio 1998-2003: 0.783\n" + ] + } + ], + "source": [ + "xyz = cl.load_sample(\"friedland_xyz_auto_bi\")\n", + "\n", + "reported_pattern = {\n", + " 12: 2.551,\n", + " 24: 1.512,\n", + " 36: 1.196,\n", + " 48: 1.085,\n", + " 60: 1.064,\n", + " 72: 1.013,\n", + " 84: 1.003,\n", + " 96: 0.992,\n", + " 108: 0.992,\n", + " 120: 0.999,\n", + " 132: 1.000,\n", + "}\n", + "paid_pattern = {\n", + " 12: 21.999,\n", + " 24: 6.569,\n", + " 36: 3.160,\n", + " 48: 2.007,\n", + " 60: 1.525,\n", + " 72: 1.268,\n", + " 84: 1.116,\n", + " 96: 1.054,\n", + " 108: 1.031,\n", + " 120: 1.014,\n", + " 132: 1.010,\n", + "}\n", + "\n", + "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", + " xyz[\"Reported Claims\"]\n", + ")\n", + "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", + " xyz[\"Paid Claims\"]\n", + ")\n", + "reported_ultimate = np.round(cl.Chainladder().fit(reported).ultimate_, 0)\n", + "paid_ultimate = np.round(cl.Chainladder().fit(paid).ultimate_, 0)\n", + "selected_ultimate = (reported_ultimate + paid_ultimate) / 2\n", + "earned_premium = xyz[\"Earned Premium\"].latest_diagonal\n", + "estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)\n", + "avg_98_03 = float(\n", + " np.round((selected_ultimate / earned_premium).iloc[:, :, 0:6, :].mean(), 3)\n", + ")\n", + "selected_claim_ratio = np.array([\n", + " 0.783,\n", + " 0.783,\n", + " 0.783,\n", + " 0.783,\n", + " 0.783,\n", + " 0.783,\n", + " 0.871,\n", + " 0.783,\n", + " 0.658,\n", + " 0.638,\n", + " 0.825,\n", + "])\n", + "sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)\n", + "el_reported = cl.ExpectedLoss(apriori=1).fit(\n", + " xyz[\"Reported Claims\"], sample_weight=sample_weight\n", + ")\n", + "el_paid = cl.ExpectedLoss(apriori=1).fit(\n", + " xyz[\"Paid Claims\"], sample_weight=sample_weight\n", + ")\n", + "expected_claims = np.round(el_reported.ultimate_, 0)\n", + "xyz_reported_ultimate = reported_ultimate\n", + "xyz_paid_ultimate = paid_ultimate\n", + "xyz_expected_claims = expected_claims\n", + "\n", + "years = list(xyz[\"Reported Claims\"].origin.year)\n", + "exhibit_iii_s1 = pd.DataFrame(index=years)\n", + "exhibit_iii_s1[\"Reported (2)\"] = as_series(\n", + " xyz[\"Reported Claims\"].latest_diagonal\n", + ").values\n", + "exhibit_iii_s1[\"Paid (3)\"] = as_series(xyz[\"Paid Claims\"].latest_diagonal).values\n", + "exhibit_iii_s1[\"CDF Reported (4)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", + ").values\n", + "exhibit_iii_s1[\"CDF Paid (5)\"] = as_series(\n", + " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", + ").values\n", + "exhibit_iii_s1[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", + "exhibit_iii_s1[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", + "exhibit_iii_s1[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", + "exhibit_iii_s1[\"Earned Premium (9)\"] = as_series(earned_premium).values\n", + "exhibit_iii_s1[\"Estimated Claim Ratio (10)\"] = as_series(estimated_claim_ratios).values\n", + "exhibit_iii_s1[\"Selected Claim Ratio (11)\"] = selected_claim_ratio\n", + "exhibit_iii_s1[\"Expected Claims (12)\"] = as_series(expected_claims).values\n", + "display(exhibit_iii_s1)\n", + "print(f\"Average estimated claim ratio 1998-2003: {avg_98_03}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "762b32c8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:57.989109Z", + "iopub.status.busy": "2026-07-23T16:32:57.988994Z", + "iopub.status.idle": "2026-07-23T16:32:57.996706Z", + "shell.execute_reply": "2026-07-23T16:32:57.994993Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit III Sheet 1 — reconcile to Friedland PDF p144\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Reported (2)\"],\n", + " [15822, 25107, 37246, 38798, 48169, 44373, 70288, 70655, 48804, 31732, 18632],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Paid (3)\"],\n", + " [15822, 24817, 36782, 38519, 44437, 39320, 52811, 40026, 22819, 11865, 3409],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Ult Reported (6)\"],\n", + " [15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Ult Paid (7)\"],\n", + " [15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Initial Selected (8)\"],\n", + " [15901, 25123, 37435, 39543, 48953, 47404, 77662, 78497, 65239, 62960, 61262],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Earned Premium (9)\"],\n", + " [20000, 31500, 45000, 50000, 61183, 69175, 99322, 138151, 107578, 62438, 47797],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Estimated Claim Ratio (10)\"],\n", + " [0.795, 0.798, 0.832, 0.791, 0.800, 0.685, 0.782, 0.568, 0.606, 1.008, 1.282],\n", + ")\n", + "assert np.isclose(avg_98_03, 0.783)\n", + "assert np.allclose(\n", + " exhibit_iii_s1[\"Expected Claims (12)\"],\n", + " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", + " atol=1,\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "86e386b0", + "metadata": {}, + "source": [ + "## P145 (Exhibit III Sheet 2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "6a86701a", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.001475Z", + "iopub.status.busy": "2026-07-23T16:32:58.001212Z", + "iopub.status.idle": "2026-07-23T16:32:58.112616Z", + "shell.execute_reply": "2026-07-23T16:32:58.112270Z" + } + }, + "outputs": [ + { + "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", + "
Initial Selected Ult (2)
200248953.0
200347404.0
200477662.0
200578496.0
200665239.0
200762960.0
200861262.0
\n", + "
" + ], + "text/plain": [ + " Initial Selected Ult (2)\n", + "2002 48953.0\n", + "2003 47404.0\n", + "2004 77662.0\n", + "2005 78496.0\n", + "2006 65239.0\n", + "2007 62960.0\n", + "2008 61262.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 6, - "id": "37cef2cb", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.706987Z", - "iopub.status.busy": "2026-07-23T16:32:57.706774Z", - "iopub.status.idle": "2026-07-23T16:32:57.818404Z", - "shell.execute_reply": "2026-07-23T16:32:57.817888Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Estimated Claim Ratio (10)Selected Claim Ratio (11)Expected Claims (12)
199847742304.047644187.01.0001.00247742304.047739475.047740890.068574209.00.6960.7551430657.0
199951185767.051000534.01.0001.00451185767.051204536.051195152.068544981.00.7470.7551408736.0
200054837929.054533225.01.0011.00654892767.054860424.054876596.068907977.00.7960.7551680983.0
200156299562.055878421.01.0031.01156468461.056493084.056480772.072544955.00.7790.7554408716.0
200258592712.057807215.01.0061.02058944268.058963359.058953814.079228887.00.7440.7559421665.0
200357565344.055930654.01.0111.04058198563.058167880.058183221.086643542.00.6720.6556318302.0
200456976657.053774672.01.0231.08558287120.058345519.058316320.091763523.00.6360.6559646290.0
200556786410.050644994.01.0511.18459682517.059963673.059823095.094115312.00.6360.6561174953.0
200654641339.043606497.01.1101.40460651886.061223522.060937704.095272279.00.6400.6561926981.0
200748853563.027229969.01.2922.39063118803.065079626.064099215.095176240.00.6730.6561864556.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Estimated Claim Ratio (10) Selected Claim Ratio (11) Expected Claims (12)\n", - "1998 47742304.0 47644187.0 1.000 1.002 47742304.0 47739475.0 47740890.0 68574209.0 0.696 0.75 51430657.0\n", - "1999 51185767.0 51000534.0 1.000 1.004 51185767.0 51204536.0 51195152.0 68544981.0 0.747 0.75 51408736.0\n", - "2000 54837929.0 54533225.0 1.001 1.006 54892767.0 54860424.0 54876596.0 68907977.0 0.796 0.75 51680983.0\n", - "2001 56299562.0 55878421.0 1.003 1.011 56468461.0 56493084.0 56480772.0 72544955.0 0.779 0.75 54408716.0\n", - "2002 58592712.0 57807215.0 1.006 1.020 58944268.0 58963359.0 58953814.0 79228887.0 0.744 0.75 59421665.0\n", - "2003 57565344.0 55930654.0 1.011 1.040 58198563.0 58167880.0 58183221.0 86643542.0 0.672 0.65 56318302.0\n", - "2004 56976657.0 53774672.0 1.023 1.085 58287120.0 58345519.0 58316320.0 91763523.0 0.636 0.65 59646290.0\n", - "2005 56786410.0 50644994.0 1.051 1.184 59682517.0 59963673.0 59823095.0 94115312.0 0.636 0.65 61174953.0\n", - "2006 54641339.0 43606497.0 1.110 1.404 60651886.0 61223522.0 60937704.0 95272279.0 0.640 0.65 61926981.0\n", - "2007 48853563.0 27229969.0 1.292 2.390 63118803.0 65079626.0 64099215.0 95176240.0 0.673 0.65 61864556.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", + "
Trend to 2004 (3)Trend to 2005 (4)Trend to 2006 (5)Trend to 2007 (6)Trend to 2008 (7)
20021.0701.1061.1441.1831.224
20031.0341.0701.1061.1441.183
20041.0001.0341.0701.1061.144
20050.9671.0001.0341.0701.106
20060.9350.9671.0001.0341.070
20070.9040.9350.9671.0001.034
20080.8740.9040.9350.9671.000
\n", + "
" ], - "source": [ - "ia = cl.load_sample(\"friedland_us_industry_auto\")\n", - "\n", - "reported_pattern = {\n", - " 12: 1.292, 24: 1.110, 36: 1.051, 48: 1.023, 60: 1.011,\n", - " 72: 1.006, 84: 1.003, 96: 1.001, 108: 1.000, 120: 1.000,\n", - "}\n", - "paid_pattern = {\n", - " 12: 2.390, 24: 1.404, 36: 1.184, 48: 1.085, 60: 1.040,\n", - " 72: 1.020, 84: 1.011, 96: 1.006, 108: 1.004, 120: 1.002,\n", - "}\n", - "\n", - "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", - " ia[\"Reported Claims\"]\n", - ")\n", - "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", - " ia[\"Paid Claims\"]\n", - ")\n", - "reported_ultimate = cl.Chainladder().fit(reported).ultimate_\n", - "paid_ultimate = cl.Chainladder().fit(paid).ultimate_\n", - "selected_ultimate = np.round((reported_ultimate + paid_ultimate) / 2, 0)\n", - "earned_premium = ia[\"Earned Premium\"].latest_diagonal\n", - "estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)\n", - "selected_claim_ratio = np.array(\n", - " [0.75, 0.75, 0.75, 0.75, 0.75, 0.65, 0.65, 0.65, 0.65, 0.65]\n", - ")\n", - "sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)\n", - "el_reported = cl.ExpectedLoss(apriori=1).fit(\n", - " ia[\"Reported Claims\"], sample_weight=sample_weight\n", - ")\n", - "el_paid = cl.ExpectedLoss(apriori=1).fit(\n", - " ia[\"Paid Claims\"], sample_weight=sample_weight\n", - ")\n", - "expected_claims = np.round(el_reported.ultimate_, 0)\n", - "\n", - "years = list(ia[\"Reported Claims\"].origin.year)\n", - "exhibit_ii_s1 = pd.DataFrame(index=years)\n", - "exhibit_ii_s1[\"Reported (2)\"] = as_series(ia[\"Reported Claims\"].latest_diagonal).values\n", - "exhibit_ii_s1[\"Paid (3)\"] = as_series(ia[\"Paid Claims\"].latest_diagonal).values\n", - "exhibit_ii_s1[\"CDF Reported (4)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", - ").values\n", - "exhibit_ii_s1[\"CDF Paid (5)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", - ").values\n", - "exhibit_ii_s1[\"Ult Reported (6)\"] = as_series(np.round(reported_ultimate, 0)).values\n", - "exhibit_ii_s1[\"Ult Paid (7)\"] = as_series(np.round(paid_ultimate, 0)).values\n", - "exhibit_ii_s1[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", - "exhibit_ii_s1[\"Earned Premium (9)\"] = as_series(earned_premium).values\n", - "exhibit_ii_s1[\"Estimated Claim Ratio (10)\"] = as_series(estimated_claim_ratios).values\n", - "exhibit_ii_s1[\"Selected Claim Ratio (11)\"] = selected_claim_ratio\n", - "exhibit_ii_s1[\"Expected Claims (12)\"] = as_series(expected_claims).values\n", - "display(exhibit_ii_s1)\n" + "text/plain": [ + " Trend to 2004 (3) Trend to 2005 (4) Trend to 2006 (5) Trend to 2007 (6) Trend to 2008 (7)\n", + "2002 1.070 1.106 1.144 1.183 1.224\n", + "2003 1.034 1.070 1.106 1.144 1.183\n", + "2004 1.000 1.034 1.070 1.106 1.144\n", + "2005 0.967 1.000 1.034 1.070 1.106\n", + "2006 0.935 0.967 1.000 1.034 1.070\n", + "2007 0.904 0.935 0.967 1.000 1.034\n", + "2008 0.874 0.904 0.935 0.967 1.000" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 7, - "id": "a62265b9", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.821111Z", - "iopub.status.busy": "2026-07-23T16:32:57.820985Z", - "iopub.status.idle": "2026-07-23T16:32:57.827979Z", - "shell.execute_reply": "2026-07-23T16:32:57.827354Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit II Sheet 1 — reconcile to Friedland PDF p142\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Reported (2)\"],\n", - " [47742304, 51185767, 54837929, 56299562, 58592712, 57565344, 56976657, 56786410, 54641339, 48853563],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Paid (3)\"],\n", - " [47644187, 51000534, 54533225, 55878421, 57807215, 55930654, 53774672, 50644994, 43606497, 27229969],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Ult Reported (6)\"],\n", - " [47742304, 51185767, 54892767, 56468461, 58944268, 58198563, 58287120, 59682517, 60651886, 63118803],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Ult Paid (7)\"],\n", - " [47739475, 51204536, 54860424, 56493084, 58963359, 58167880, 58345519, 59963673, 61223522, 65079626],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Initial Selected (8)\"],\n", - " [47740890, 51195152, 54876596, 56480772, 58953814, 58183221, 58316320, 59823095, 60937704, 64099215],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Earned Premium (9)\"],\n", - " [68574209, 68544981, 68907977, 72544955, 79228887, 86643542, 91763523, 94115312, 95272279, 95176240],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Estimated Claim Ratio (10)\"],\n", - " [0.696, 0.747, 0.796, 0.779, 0.744, 0.672, 0.636, 0.636, 0.640, 0.673],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s1[\"Expected Claims (12)\"],\n", - " [51430657, 51408736, 51680983, 54408716, 59421665, 56318302, 59646290, 61174953, 61926981, 61864556],\n", - ")\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", + "
Tort to 2004 (8)Tort to 2005 (9)Tort to 2006 (10)Tort to 2007 (11)Tort to 2008 (12)
20021.0001.0000.8930.670.67
20031.0001.0000.8930.670.67
20041.0001.0000.8930.670.67
20051.0001.0000.8930.670.67
20061.1191.1191.0000.750.75
20071.4931.4931.3331.001.00
20081.4931.4931.3331.001.00
\n", + "
" + ], + "text/plain": [ + " Tort to 2004 (8) Tort to 2005 (9) Tort to 2006 (10) Tort to 2007 (11) Tort to 2008 (12)\n", + "2002 1.000 1.000 0.893 0.67 0.67\n", + "2003 1.000 1.000 0.893 0.67 0.67\n", + "2004 1.000 1.000 0.893 0.67 0.67\n", + "2005 1.000 1.000 0.893 0.67 0.67\n", + "2006 1.119 1.119 1.000 0.75 0.75\n", + "2007 1.493 1.493 1.333 1.00 1.00\n", + "2008 1.493 1.493 1.333 1.00 1.00" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "c14233ed", - "metadata": {}, - "source": [ - "## P143 (Exhibit II Sheet 2)\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", + "
Earned Premium (13)
200261183.0
200369175.0
200499322.0
2005138151.0
2006107578.0
200762438.0
200847797.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (13)\n", + "2002 61183.0\n", + "2003 69175.0\n", + "2004 99322.0\n", + "2005 138151.0\n", + "2006 107578.0\n", + "2007 62438.0\n", + "2008 47797.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 8, - "id": "fc3209f1", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.830544Z", - "iopub.status.busy": "2026-07-23T16:32:57.830274Z", - "iopub.status.idle": "2026-07-23T16:32:57.850125Z", - "shell.execute_reply": "2026-07-23T16:32:57.849826Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
199847742304.047644187.051430657.098117.03688353.03786470.0
199951185767.051000534.051408736.0185233.0222969.0408202.0
200054837929.054533225.051680983.0304704.0-3156946.0-2852242.0
200156299562.055878421.054408716.0421141.0-1890846.0-1469705.0
200258592712.057807215.059421665.0785497.0828953.01614450.0
200357565344.055930654.056318302.01634690.0-1247042.0387648.0
200456976657.053774672.059646290.03201985.02669633.05871618.0
200556786410.050644994.061174953.06141416.04388543.010529959.0
200654641339.043606497.061926981.011034842.07285642.018320484.0
200748853563.027229969.061864556.021623594.013010993.034634587.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", - "1998 47742304.0 47644187.0 51430657.0 98117.0 3688353.0 3786470.0\n", - "1999 51185767.0 51000534.0 51408736.0 185233.0 222969.0 408202.0\n", - "2000 54837929.0 54533225.0 51680983.0 304704.0 -3156946.0 -2852242.0\n", - "2001 56299562.0 55878421.0 54408716.0 421141.0 -1890846.0 -1469705.0\n", - "2002 58592712.0 57807215.0 59421665.0 785497.0 828953.0 1614450.0\n", - "2003 57565344.0 55930654.0 56318302.0 1634690.0 -1247042.0 387648.0\n", - "2004 56976657.0 53774672.0 59646290.0 3201985.0 2669633.0 5871618.0\n", - "2005 56786410.0 50644994.0 61174953.0 6141416.0 4388543.0 10529959.0\n", - "2006 54641339.0 43606497.0 61926981.0 11034842.0 7285642.0 18320484.0\n", - "2007 48853563.0 27229969.0 61864556.0 21623594.0 13010993.0 34634587.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", - "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
Total543481587.0498050368.0569281839.045431219.025800252.071231471.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", - "Total 543481587.0 498050368.0 569281839.0 45431219.0 25800252.0 71231471.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", + "
Rate to 2004 (14)Rate to 2005 (15)Rate to 2006 (16)Rate to 2007 (17)Rate to 2008 (18)
20021.1291.2981.4281.1420.914
20031.0751.2361.3601.0880.870
20041.0001.1501.2651.0120.810
20050.8701.0001.1000.8800.704
20060.7910.9091.0000.8000.640
20070.9881.1361.2501.0000.800
20081.2351.4201.5621.2501.000
\n", + "
" ], - "source": [ - "exhibit_ii_s2 = unpaid_exhibit(ia[\"Reported Claims\"], ia[\"Paid Claims\"], expected_claims)\n", - "display(exhibit_ii_s2)\n", - "display(exhibit_ii_s2.sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Rate to 2004 (14) Rate to 2005 (15) Rate to 2006 (16) Rate to 2007 (17) Rate to 2008 (18)\n", + "2002 1.129 1.298 1.428 1.142 0.914\n", + "2003 1.075 1.236 1.360 1.088 0.870\n", + "2004 1.000 1.150 1.265 1.012 0.810\n", + "2005 0.870 1.000 1.100 0.880 0.704\n", + "2006 0.791 0.909 1.000 0.800 0.640\n", + "2007 0.988 1.136 1.250 1.000 0.800\n", + "2008 1.235 1.420 1.562 1.250 1.000" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 9, - "id": "e206f932", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.852655Z", - "iopub.status.busy": "2026-07-23T16:32:57.852317Z", - "iopub.status.idle": "2026-07-23T16:32:57.858563Z", - "shell.execute_reply": "2026-07-23T16:32:57.857022Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit II Sheet 2 — reconcile to Friedland PDF p143\n", - "assert np.allclose(\n", - " exhibit_ii_s2[\"Case Outstanding (5)\"],\n", - " [98117, 185233, 304704, 421141, 785497, 1634690, 3201985, 6141416, 11034842, 21623594],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s2[\"IBNR (6)\"],\n", - " [3688353, 222969, -3156946, -1890846, 828953, -1247042, 2669633, 4388543, 7285642, 13010993],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_ii_s2[\"Total Unpaid (7)\"],\n", - " [3786470, 408202, -2852242, -1469705, 1614450, 387648, 5871618, 10529959, 18320484, 34634587],\n", - ")\n", - "assert np.isclose(exhibit_ii_s2[\"Reported (2)\"].sum(), 543481587)\n", - "assert np.isclose(exhibit_ii_s2[\"Paid (3)\"].sum(), 498050368)\n", - "assert np.isclose(exhibit_ii_s2[\"Expected Claims (4)\"].sum(), 569281839)\n", - "assert np.isclose(exhibit_ii_s2[\"Case Outstanding (5)\"].sum(), 45431219)\n", - "assert np.isclose(exhibit_ii_s2[\"IBNR (6)\"].sum(), 25800252)\n", - "assert np.isclose(exhibit_ii_s2[\"Total Unpaid (7)\"].sum(), 71231471)\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", + "
OL Claim Ratio 2004 (19)OL Claim Ratio 2005 (20)OL Claim Ratio 2006 (21)OL Claim Ratio 2007 (22)OL Claim Ratio 2008 (23)
20020.7580.6820.5730.5550.718
20030.6590.5930.4980.4830.624
20040.7820.7030.5910.5730.740
20050.6320.5680.4770.4630.598
20060.8030.7220.6060.5880.760
20071.3771.2381.0401.0081.304
20081.3541.2171.0220.9911.282
\n", + "
" + ], + "text/plain": [ + " OL Claim Ratio 2004 (19) OL Claim Ratio 2005 (20) OL Claim Ratio 2006 (21) OL Claim Ratio 2007 (22) OL Claim Ratio 2008 (23)\n", + "2002 0.758 0.682 0.573 0.555 0.718\n", + "2003 0.659 0.593 0.498 0.483 0.624\n", + "2004 0.782 0.703 0.591 0.573 0.740\n", + "2005 0.632 0.568 0.477 0.463 0.598\n", + "2006 0.803 0.722 0.606 0.588 0.760\n", + "2007 1.377 1.238 1.040 1.008 1.304\n", + "2008 1.354 1.217 1.022 0.991 1.282" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "27f7e58a", - "metadata": {}, - "source": [ - "## P144 (Exhibit III Sheet 1)\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", + "
20042005200620072008
All Years (24)0.9090.8180.6870.6660.861
All Years ex Hi/Lo (24)0.8710.7830.6580.6380.825
Latest 5 (24)0.9890.8900.7470.7250.937
Latest 3 (24)1.1781.0590.8900.8631.115
Selected (25)0.8710.7830.6580.6380.825
\n", + "
" + ], + "text/plain": [ + " 2004 2005 2006 2007 2008\n", + "All Years (24) 0.909 0.818 0.687 0.666 0.861\n", + "All Years ex Hi/Lo (24) 0.871 0.783 0.658 0.638 0.825\n", + "Latest 5 (24) 0.989 0.890 0.747 0.725 0.937\n", + "Latest 3 (24) 1.178 1.059 0.890 0.863 1.115\n", + "Selected (25) 0.871 0.783 0.658 0.638 0.825" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def relative_level_triangle(base, years=range(2004, 2009)):\n", + " pieces = []\n", + " for year in years:\n", + " rel = base / base.loc[:, :, str(year), :]\n", + " rel.columns = [str(year)]\n", + " pieces.append(rel)\n", + " return cl.concat(pieces, axis=1)\n", + "\n", + "\n", + "selected_view = np.round(selected_ultimate.iloc[:, :, 4:, :], 0)\n", + "earned_premium_view = earned_premium.iloc[:, :, 4:, :]\n", + "\n", + "base_trend_2008 = (\n", + " cl\n", + " .Trend(trends=[0.03425], dates=[(\"2008-12-31\", \"2002-01-01\")])\n", + " .fit(selected_view)\n", + " .trend_\n", + ")\n", + "severity_trend_adjustment = relative_level_triangle(base_trend_2008)\n", + "\n", + "base_tort_2008 = (\n", + " cl\n", + " .Trend(\n", + " trends=[-0.25, 0.670 / 0.75 - 1],\n", + " dates=[(\"2007-12-31\", \"2006-12-31\"), (\"2006-12-31\", \"2005-12-31\")],\n", + " )\n", + " .fit(selected_view)\n", + " .trend_\n", + ")\n", + "tort_reform_adjustment = relative_level_triangle(base_tort_2008)\n", + "\n", + "rate_changes = [0, 0.05, 0.075, 0.15, 0.1, -0.2, -0.2]\n", + "olf = cl.parallelogram_olf(\n", + " rate_changes,\n", + " pd.to_datetime([f\"{y}-01-01\" for y in range(2002, 2009)]),\n", + " vertical_line=True,\n", + ")[\"OLF\"].values\n", + "base_rate = selected_view * 0 + olf.reshape(selected_view.shape)\n", + "rate_level_adjustment = relative_level_triangle(base_rate)\n", + "\n", + "adjusted_claim_ratios = (\n", + " selected_view * severity_trend_adjustment * tort_reform_adjustment\n", + ") / (earned_premium_view * rate_level_adjustment)\n", + "\n", + "ay = list(range(2002, 2009))\n", + "ty = list(range(2004, 2009))\n", + "\n", + "exhibit_iii_s2_ult = pd.DataFrame(\n", + " {\"Initial Selected Ult (2)\": as_series(selected_view).values}, index=ay\n", + ")\n", + "display(exhibit_iii_s2_ult)\n", + "\n", + "exhibit_iii_s2_trend = pd.DataFrame(\n", + " np.round(severity_trend_adjustment.values.squeeze().T, 3),\n", + " index=ay,\n", + " columns=[f\"Trend to {y} ({n})\" for y, n in zip(ty, [3, 4, 5, 6, 7])],\n", + ")\n", + "display(exhibit_iii_s2_trend)\n", + "\n", + "exhibit_iii_s2_tort = pd.DataFrame(\n", + " np.round(tort_reform_adjustment.values.squeeze().T, 3),\n", + " index=ay,\n", + " columns=[f\"Tort to {y} ({n})\" for y, n in zip(ty, [8, 9, 10, 11, 12])],\n", + ")\n", + "display(exhibit_iii_s2_tort)\n", + "\n", + "exhibit_iii_s2_prem = pd.DataFrame(\n", + " {\"Earned Premium (13)\": as_series(earned_premium_view).values}, index=ay\n", + ")\n", + "display(exhibit_iii_s2_prem)\n", + "\n", + "exhibit_iii_s2_rate = pd.DataFrame(\n", + " np.round(rate_level_adjustment.values.squeeze().T, 3),\n", + " index=ay,\n", + " columns=[f\"Rate to {y} ({n})\" for y, n in zip(ty, [14, 15, 16, 17, 18])],\n", + ")\n", + "display(exhibit_iii_s2_rate)\n", + "\n", + "exhibit_iii_s2_lr = pd.DataFrame(\n", + " np.round(adjusted_claim_ratios.values.squeeze().T, 3),\n", + " index=ay,\n", + " columns=[f\"OL Claim Ratio {y} ({n})\" for y, n in zip(ty, [19, 20, 21, 22, 23])],\n", + ")\n", + "display(exhibit_iii_s2_lr)\n", + "\n", + "vals = adjusted_claim_ratios.values.squeeze()\n", + "all_years = adjusted_claim_ratios.mean(axis=\"origin\").values.squeeze()\n", + "ex_high_low = (vals.sum(axis=1) - vals.max(axis=1) - vals.min(axis=1)) / 5\n", + "latest_5 = (\n", + " adjusted_claim_ratios.loc[:, :, \"2004\":, :].mean(axis=\"origin\").values.squeeze()\n", + ")\n", + "latest_3 = (\n", + " adjusted_claim_ratios.loc[:, :, \"2006\":, :].mean(axis=\"origin\").values.squeeze()\n", + ")\n", + "average_claim_ratios = np.vstack([all_years, ex_high_low, latest_5, latest_3])\n", + "selected_expected_claim_ratio = ex_high_low\n", + "\n", + "exhibit_iii_s2_avgs = pd.DataFrame(\n", + " np.round(average_claim_ratios, 3),\n", + " index=[\n", + " \"All Years (24)\",\n", + " \"All Years ex Hi/Lo (24)\",\n", + " \"Latest 5 (24)\",\n", + " \"Latest 3 (24)\",\n", + " ],\n", + " columns=ty,\n", + ")\n", + "exhibit_iii_s2_avgs.loc[\"Selected (25)\"] = np.round(selected_expected_claim_ratio, 3)\n", + "display(exhibit_iii_s2_avgs)" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "9c4d3bd9", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.114731Z", + "iopub.status.busy": "2026-07-23T16:32:58.114586Z", + "iopub.status.idle": "2026-07-23T16:32:58.122532Z", + "shell.execute_reply": "2026-07-23T16:32:58.121977Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit III Sheet 2 — reconcile to Friedland PDF p145\n", + "assert np.allclose(\n", + " as_series(selected_view).values,\n", + " [48953, 47404, 77662, 78497, 65239, 62960, 61262],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s2_trend.values,\n", + " [\n", + " [1.070, 1.106, 1.144, 1.183, 1.224],\n", + " [1.034, 1.070, 1.106, 1.144, 1.183],\n", + " [1.000, 1.034, 1.070, 1.106, 1.144],\n", + " [0.967, 1.000, 1.034, 1.070, 1.106],\n", + " [0.935, 0.967, 1.000, 1.034, 1.070],\n", + " [0.904, 0.935, 0.967, 1.000, 1.034],\n", + " [0.874, 0.904, 0.935, 0.967, 1.000],\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s2_tort.values,\n", + " [\n", + " [1.000, 1.000, 0.893, 0.670, 0.670],\n", + " [1.000, 1.000, 0.893, 0.670, 0.670],\n", + " [1.000, 1.000, 0.893, 0.670, 0.670],\n", + " [1.000, 1.000, 0.893, 0.670, 0.670],\n", + " [1.119, 1.119, 1.000, 0.750, 0.750],\n", + " [1.493, 1.493, 1.333, 1.000, 1.000],\n", + " [1.493, 1.493, 1.333, 1.000, 1.000],\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s2_prem[\"Earned Premium (13)\"],\n", + " [61183, 69175, 99322, 138151, 107578, 62438, 47797],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s2_rate.values,\n", + " [\n", + " [1.129, 1.298, 1.428, 1.142, 0.914],\n", + " [1.075, 1.236, 1.360, 1.088, 0.870],\n", + " [1.000, 1.150, 1.265, 1.012, 0.810],\n", + " [0.870, 1.000, 1.100, 0.880, 0.704],\n", + " [0.791, 0.909, 1.000, 0.800, 0.640],\n", + " [0.988, 1.136, 1.250, 1.000, 0.800],\n", + " [1.235, 1.420, 1.562, 1.250, 1.000],\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s2_lr.values,\n", + " [\n", + " [0.758, 0.682, 0.573, 0.555, 0.718],\n", + " [0.659, 0.593, 0.498, 0.483, 0.624],\n", + " [0.782, 0.703, 0.591, 0.573, 0.740],\n", + " [0.632, 0.568, 0.477, 0.463, 0.598],\n", + " [0.803, 0.722, 0.606, 0.588, 0.760],\n", + " [1.377, 1.238, 1.040, 1.008, 1.304],\n", + " [1.354, 1.217, 1.022, 0.991, 1.282],\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " np.round(average_claim_ratios, 3),\n", + " [\n", + " [0.909, 0.818, 0.687, 0.666, 0.861],\n", + " [0.871, 0.783, 0.658, 0.638, 0.825],\n", + " [0.989, 0.890, 0.747, 0.725, 0.937],\n", + " [1.178, 1.059, 0.890, 0.863, 1.115],\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " np.round(selected_expected_claim_ratio, 3), [0.871, 0.783, 0.658, 0.638, 0.825]\n", + ")" + ] + }, + { + "cell_type": "markdown", + "id": "e42bf89a", + "metadata": {}, + "source": [ + "## P146 (Exhibit III Sheet 3)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "b3874bf4", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.126283Z", + "iopub.status.busy": "2026-07-23T16:32:58.126065Z", + "iopub.status.idle": "2026-07-23T16:32:58.145115Z", + "shell.execute_reply": "2026-07-23T16:32:58.144576Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 10, - "id": "d62ffc9c", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.860893Z", - "iopub.status.busy": "2026-07-23T16:32:57.860718Z", - "iopub.status.idle": "2026-07-23T16:32:57.986704Z", - "shell.execute_reply": "2026-07-23T16:32:57.985327Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)CDF Reported (4)CDF Paid (5)Ult Reported (6)Ult Paid (7)Initial Selected (8)Earned Premium (9)Estimated Claim Ratio (10)Selected Claim Ratio (11)Expected Claims (12)
199815822.015822.01.0001.01015822.015980.015901.020000.00.7950.78315660.0
199925107.024817.00.9991.01425082.025164.025123.031500.00.7980.78324664.0
200037246.036782.00.9921.03136948.037922.037435.045000.00.8320.78335235.0
200138798.038519.00.9921.05438488.040599.039543.550000.00.7910.78339150.0
200248169.044437.01.0031.11648314.049592.048953.061183.00.8000.78347906.0
200344373.039320.01.0131.26844950.049858.047404.069175.00.6850.78354164.0
200470288.052811.01.0641.52574786.080537.077661.599322.00.7820.87186509.0
200570655.040026.01.0852.00776661.080332.078496.5138151.00.5680.783108172.0
200648804.022819.01.1963.16058370.072108.065239.0107578.00.6060.65870786.0
200731732.011865.01.5126.56947979.077941.062960.062438.01.0080.63839835.0
200818632.03409.02.55121.99947530.074995.061262.547797.01.2820.82539433.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) CDF Reported (4) CDF Paid (5) Ult Reported (6) Ult Paid (7) Initial Selected (8) Earned Premium (9) Estimated Claim Ratio (10) Selected Claim Ratio (11) Expected Claims (12)\n", - "1998 15822.0 15822.0 1.000 1.010 15822.0 15980.0 15901.0 20000.0 0.795 0.783 15660.0\n", - "1999 25107.0 24817.0 0.999 1.014 25082.0 25164.0 25123.0 31500.0 0.798 0.783 24664.0\n", - "2000 37246.0 36782.0 0.992 1.031 36948.0 37922.0 37435.0 45000.0 0.832 0.783 35235.0\n", - "2001 38798.0 38519.0 0.992 1.054 38488.0 40599.0 39543.5 50000.0 0.791 0.783 39150.0\n", - "2002 48169.0 44437.0 1.003 1.116 48314.0 49592.0 48953.0 61183.0 0.800 0.783 47906.0\n", - "2003 44373.0 39320.0 1.013 1.268 44950.0 49858.0 47404.0 69175.0 0.685 0.783 54164.0\n", - "2004 70288.0 52811.0 1.064 1.525 74786.0 80537.0 77661.5 99322.0 0.782 0.871 86509.0\n", - "2005 70655.0 40026.0 1.085 2.007 76661.0 80332.0 78496.5 138151.0 0.568 0.783 108172.0\n", - "2006 48804.0 22819.0 1.196 3.160 58370.0 72108.0 65239.0 107578.0 0.606 0.658 70786.0\n", - "2007 31732.0 11865.0 1.512 6.569 47979.0 77941.0 62960.0 62438.0 1.008 0.638 39835.0\n", - "2008 18632.0 3409.0 2.551 21.999 47530.0 74995.0 61262.5 47797.0 1.282 0.825 39433.0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Average estimated claim ratio 1998-2003: 0.783\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", + "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
199815822.015822.015660.00.0-162.0-162.0
199925107.024817.024664.0290.0-443.0-153.0
200037246.036782.035235.0464.0-2011.0-1547.0
200138798.038519.039150.0279.0352.0631.0
200248169.044437.047906.03732.0-263.03469.0
200344373.039320.054164.05053.09791.014844.0
200470288.052811.086509.017477.016221.033698.0
200570655.040026.0108172.030629.037517.068146.0
200648804.022819.070786.025985.021982.047967.0
200731732.011865.039835.019867.08103.027970.0
200818632.03409.039433.015223.020801.036024.0
\n", + "
" ], - "source": [ - "xyz = cl.load_sample(\"friedland_xyz_auto_bi\")\n", - "\n", - "reported_pattern = {\n", - " 12: 2.551, 24: 1.512, 36: 1.196, 48: 1.085, 60: 1.064, 72: 1.013,\n", - " 84: 1.003, 96: 0.992, 108: 0.992, 120: 0.999, 132: 1.000,\n", - "}\n", - "paid_pattern = {\n", - " 12: 21.999, 24: 6.569, 36: 3.160, 48: 2.007, 60: 1.525, 72: 1.268,\n", - " 84: 1.116, 96: 1.054, 108: 1.031, 120: 1.014, 132: 1.010,\n", - "}\n", - "\n", - "reported = cl.DevelopmentConstant(patterns=reported_pattern, style=\"cdf\").fit_transform(\n", - " xyz[\"Reported Claims\"]\n", - ")\n", - "paid = cl.DevelopmentConstant(patterns=paid_pattern, style=\"cdf\").fit_transform(\n", - " xyz[\"Paid Claims\"]\n", - ")\n", - "reported_ultimate = np.round(cl.Chainladder().fit(reported).ultimate_, 0)\n", - "paid_ultimate = np.round(cl.Chainladder().fit(paid).ultimate_, 0)\n", - "selected_ultimate = (reported_ultimate + paid_ultimate) / 2\n", - "earned_premium = xyz[\"Earned Premium\"].latest_diagonal\n", - "estimated_claim_ratios = np.round(selected_ultimate / earned_premium, 3)\n", - "avg_98_03 = float(np.round((selected_ultimate / earned_premium).iloc[:, :, 0:6, :].mean(), 3))\n", - "selected_claim_ratio = np.array(\n", - " [0.783, 0.783, 0.783, 0.783, 0.783, 0.783, 0.871, 0.783, 0.658, 0.638, 0.825]\n", - ")\n", - "sample_weight = earned_premium * selected_claim_ratio.reshape(1, 1, -1, 1)\n", - "el_reported = cl.ExpectedLoss(apriori=1).fit(\n", - " xyz[\"Reported Claims\"], sample_weight=sample_weight\n", - ")\n", - "el_paid = cl.ExpectedLoss(apriori=1).fit(\n", - " xyz[\"Paid Claims\"], sample_weight=sample_weight\n", - ")\n", - "expected_claims = np.round(el_reported.ultimate_, 0)\n", - "xyz_reported_ultimate = reported_ultimate\n", - "xyz_paid_ultimate = paid_ultimate\n", - "xyz_expected_claims = expected_claims\n", - "\n", - "years = list(xyz[\"Reported Claims\"].origin.year)\n", - "exhibit_iii_s1 = pd.DataFrame(index=years)\n", - "exhibit_iii_s1[\"Reported (2)\"] = as_series(xyz[\"Reported Claims\"].latest_diagonal).values\n", - "exhibit_iii_s1[\"Paid (3)\"] = as_series(xyz[\"Paid Claims\"].latest_diagonal).values\n", - "exhibit_iii_s1[\"CDF Reported (4)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(reported))[\"CDF\"]\n", - ").values\n", - "exhibit_iii_s1[\"CDF Paid (5)\"] = as_series(\n", - " cl.model_diagnostics(cl.Chainladder().fit(paid))[\"CDF\"]\n", - ").values\n", - "exhibit_iii_s1[\"Ult Reported (6)\"] = as_series(reported_ultimate).values\n", - "exhibit_iii_s1[\"Ult Paid (7)\"] = as_series(paid_ultimate).values\n", - "exhibit_iii_s1[\"Initial Selected (8)\"] = as_series(selected_ultimate).values\n", - "exhibit_iii_s1[\"Earned Premium (9)\"] = as_series(earned_premium).values\n", - "exhibit_iii_s1[\"Estimated Claim Ratio (10)\"] = as_series(estimated_claim_ratios).values\n", - "exhibit_iii_s1[\"Selected Claim Ratio (11)\"] = selected_claim_ratio\n", - "exhibit_iii_s1[\"Expected Claims (12)\"] = as_series(expected_claims).values\n", - "display(exhibit_iii_s1)\n", - "print(f\"Average estimated claim ratio 1998-2003: {avg_98_03}\")\n" + "text/plain": [ + " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", + "1998 15822.0 15822.0 15660.0 0.0 -162.0 -162.0\n", + "1999 25107.0 24817.0 24664.0 290.0 -443.0 -153.0\n", + "2000 37246.0 36782.0 35235.0 464.0 -2011.0 -1547.0\n", + "2001 38798.0 38519.0 39150.0 279.0 352.0 631.0\n", + "2002 48169.0 44437.0 47906.0 3732.0 -263.0 3469.0\n", + "2003 44373.0 39320.0 54164.0 5053.0 9791.0 14844.0\n", + "2004 70288.0 52811.0 86509.0 17477.0 16221.0 33698.0\n", + "2005 70655.0 40026.0 108172.0 30629.0 37517.0 68146.0\n", + "2006 48804.0 22819.0 70786.0 25985.0 21982.0 47967.0\n", + "2007 31732.0 11865.0 39835.0 19867.0 8103.0 27970.0\n", + "2008 18632.0 3409.0 39433.0 15223.0 20801.0 36024.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 11, - "id": "762b32c8", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:57.989109Z", - "iopub.status.busy": "2026-07-23T16:32:57.988994Z", - "iopub.status.idle": "2026-07-23T16:32:57.996706Z", - "shell.execute_reply": "2026-07-23T16:32:57.994993Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit III Sheet 1 — reconcile to Friedland PDF p144\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Reported (2)\"],\n", - " [15822, 25107, 37246, 38798, 48169, 44373, 70288, 70655, 48804, 31732, 18632],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Paid (3)\"],\n", - " [15822, 24817, 36782, 38519, 44437, 39320, 52811, 40026, 22819, 11865, 3409],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Ult Reported (6)\"],\n", - " [15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Ult Paid (7)\"],\n", - " [15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Initial Selected (8)\"],\n", - " [15901, 25123, 37435, 39543, 48953, 47404, 77662, 78497, 65239, 62960, 61262],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Earned Premium (9)\"],\n", - " [20000, 31500, 45000, 50000, 61183, 69175, 99322, 138151, 107578, 62438, 47797],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Estimated Claim Ratio (10)\"],\n", - " [0.795, 0.798, 0.832, 0.791, 0.800, 0.685, 0.782, 0.568, 0.606, 1.008, 1.282],\n", - ")\n", - "assert np.isclose(avg_98_03, 0.783)\n", - "assert np.allclose(\n", - " exhibit_iii_s1[\"Expected Claims (12)\"],\n", - " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", - " atol=1,\n", - ")\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", + "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
Total449626.0330627.0561514.0118999.0111888.0230887.0
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", + "Total 449626.0 330627.0 561514.0 118999.0 111888.0 230887.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "exhibit_iii_s3 = unpaid_exhibit(\n", + " xyz[\"Reported Claims\"], xyz[\"Paid Claims\"], xyz_expected_claims\n", + ")\n", + "display(exhibit_iii_s3)\n", + "display(exhibit_iii_s3.sum().rename(\"Total\").to_frame().T)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "27a8fcc2", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.150010Z", + "iopub.status.busy": "2026-07-23T16:32:58.149668Z", + "iopub.status.idle": "2026-07-23T16:32:58.154711Z", + "shell.execute_reply": "2026-07-23T16:32:58.154309Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit III Sheet 3 — reconcile to Friedland PDF p146\n", + "assert np.allclose(\n", + " exhibit_iii_s3[\"Expected Claims (4)\"],\n", + " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s3[\"Case Outstanding (5)\"],\n", + " [0, 290, 465, 278, 3731, 5052, 17477, 30629, 25985, 19867, 15223],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s3[\"IBNR (6)\"],\n", + " [-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s3[\"Total Unpaid (7)\"],\n", + " [-162, -152, -1547, 631, 3469, 14844, 33698, 68146, 47967, 27970, 36024],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_iii_s3[\"Reported (2)\"].sum(), 449626)\n", + "assert np.isclose(exhibit_iii_s3[\"Paid (3)\"].sum(), 330629)\n", + "assert np.isclose(exhibit_iii_s3[\"Expected Claims (4)\"].sum(), 561516, atol=1)\n", + "assert np.isclose(exhibit_iii_s3[\"Case Outstanding (5)\"].sum(), 118997, atol=1)\n", + "assert np.isclose(exhibit_iii_s3[\"IBNR (6)\"].sum(), 111890, atol=1)\n", + "assert np.isclose(exhibit_iii_s3[\"Total Unpaid (7)\"].sum(), 230887, atol=1)" + ] + }, + { + "cell_type": "markdown", + "id": "6f43d18f", + "metadata": {}, + "source": [ + "## P147 (Exhibit III Sheet 4)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "d2eeae83", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.157170Z", + "iopub.status.busy": "2026-07-23T16:32:58.156968Z", + "iopub.status.idle": "2026-07-23T16:32:58.168591Z", + "shell.execute_reply": "2026-07-23T16:32:58.168148Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "86e386b0", - "metadata": {}, - "source": [ - "## P145 (Exhibit III Sheet 2)\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", + "
Reported (2)Paid (3)Dev Ult Reported (4)Dev Ult Paid (5)Expected Claims (6)
199815822.015822.015822.015980.015660.0
199925107.024817.025082.025164.024664.0
200037246.036782.036948.037922.035235.0
200138798.038519.038488.040599.039150.0
200248169.044437.048314.049592.047906.0
200344373.039320.044950.049858.054164.0
200470288.052811.074786.080537.086509.0
200570655.040026.076661.080332.0108172.0
200648804.022819.058370.072108.070786.0
200731732.011865.047979.077941.039835.0
200818632.03409.047530.074995.039433.0
\n", + "
" + ], + "text/plain": [ + " Reported (2) Paid (3) Dev Ult Reported (4) Dev Ult Paid (5) Expected Claims (6)\n", + "1998 15822.0 15822.0 15822.0 15980.0 15660.0\n", + "1999 25107.0 24817.0 25082.0 25164.0 24664.0\n", + "2000 37246.0 36782.0 36948.0 37922.0 35235.0\n", + "2001 38798.0 38519.0 38488.0 40599.0 39150.0\n", + "2002 48169.0 44437.0 48314.0 49592.0 47906.0\n", + "2003 44373.0 39320.0 44950.0 49858.0 54164.0\n", + "2004 70288.0 52811.0 74786.0 80537.0 86509.0\n", + "2005 70655.0 40026.0 76661.0 80332.0 108172.0\n", + "2006 48804.0 22819.0 58370.0 72108.0 70786.0\n", + "2007 31732.0 11865.0 47979.0 77941.0 39835.0\n", + "2008 18632.0 3409.0 47530.0 74995.0 39433.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 12, - "id": "6a86701a", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.001475Z", - "iopub.status.busy": "2026-07-23T16:32:58.001212Z", - "iopub.status.idle": "2026-07-23T16:32:58.112616Z", - "shell.execute_reply": "2026-07-23T16:32:58.112270Z" - } - }, - "outputs": [ - { - "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", - "
Initial Selected Ult (2)
200248953.0
200347404.0
200477662.0
200578496.0
200665239.0
200762960.0
200861262.0
\n", - "
" - ], - "text/plain": [ - " Initial Selected Ult (2)\n", - "2002 48953.0\n", - "2003 47404.0\n", - "2004 77662.0\n", - "2005 78496.0\n", - "2006 65239.0\n", - "2007 62960.0\n", - "2008 61262.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", - "
Trend to 2004 (3)Trend to 2005 (4)Trend to 2006 (5)Trend to 2007 (6)Trend to 2008 (7)
20021.0701.1061.1441.1831.224
20031.0341.0701.1061.1441.183
20041.0001.0341.0701.1061.144
20050.9671.0001.0341.0701.106
20060.9350.9671.0001.0341.070
20070.9040.9350.9671.0001.034
20080.8740.9040.9350.9671.000
\n", - "
" - ], - "text/plain": [ - " Trend to 2004 (3) Trend to 2005 (4) Trend to 2006 (5) Trend to 2007 (6) Trend to 2008 (7)\n", - "2002 1.070 1.106 1.144 1.183 1.224\n", - "2003 1.034 1.070 1.106 1.144 1.183\n", - "2004 1.000 1.034 1.070 1.106 1.144\n", - "2005 0.967 1.000 1.034 1.070 1.106\n", - "2006 0.935 0.967 1.000 1.034 1.070\n", - "2007 0.904 0.935 0.967 1.000 1.034\n", - "2008 0.874 0.904 0.935 0.967 1.000" - ] - }, - "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", - "
Tort to 2004 (8)Tort to 2005 (9)Tort to 2006 (10)Tort to 2007 (11)Tort to 2008 (12)
20021.0001.0000.8930.670.67
20031.0001.0000.8930.670.67
20041.0001.0000.8930.670.67
20051.0001.0000.8930.670.67
20061.1191.1191.0000.750.75
20071.4931.4931.3331.001.00
20081.4931.4931.3331.001.00
\n", - "
" - ], - "text/plain": [ - " Tort to 2004 (8) Tort to 2005 (9) Tort to 2006 (10) Tort to 2007 (11) Tort to 2008 (12)\n", - "2002 1.000 1.000 0.893 0.67 0.67\n", - "2003 1.000 1.000 0.893 0.67 0.67\n", - "2004 1.000 1.000 0.893 0.67 0.67\n", - "2005 1.000 1.000 0.893 0.67 0.67\n", - "2006 1.119 1.119 1.000 0.75 0.75\n", - "2007 1.493 1.493 1.333 1.00 1.00\n", - "2008 1.493 1.493 1.333 1.00 1.00" - ] - }, - "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", - "
Earned Premium (13)
200261183.0
200369175.0
200499322.0
2005138151.0
2006107578.0
200762438.0
200847797.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (13)\n", - "2002 61183.0\n", - "2003 69175.0\n", - "2004 99322.0\n", - "2005 138151.0\n", - "2006 107578.0\n", - "2007 62438.0\n", - "2008 47797.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", - "
Rate to 2004 (14)Rate to 2005 (15)Rate to 2006 (16)Rate to 2007 (17)Rate to 2008 (18)
20021.1291.2981.4281.1420.914
20031.0751.2361.3601.0880.870
20041.0001.1501.2651.0120.810
20050.8701.0001.1000.8800.704
20060.7910.9091.0000.8000.640
20070.9881.1361.2501.0000.800
20081.2351.4201.5621.2501.000
\n", - "
" - ], - "text/plain": [ - " Rate to 2004 (14) Rate to 2005 (15) Rate to 2006 (16) Rate to 2007 (17) Rate to 2008 (18)\n", - "2002 1.129 1.298 1.428 1.142 0.914\n", - "2003 1.075 1.236 1.360 1.088 0.870\n", - "2004 1.000 1.150 1.265 1.012 0.810\n", - "2005 0.870 1.000 1.100 0.880 0.704\n", - "2006 0.791 0.909 1.000 0.800 0.640\n", - "2007 0.988 1.136 1.250 1.000 0.800\n", - "2008 1.235 1.420 1.562 1.250 1.000" - ] - }, - "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", - "
OL Claim Ratio 2004 (19)OL Claim Ratio 2005 (20)OL Claim Ratio 2006 (21)OL Claim Ratio 2007 (22)OL Claim Ratio 2008 (23)
20020.7580.6820.5730.5550.718
20030.6590.5930.4980.4830.624
20040.7820.7030.5910.5730.740
20050.6320.5680.4770.4630.598
20060.8030.7220.6060.5880.760
20071.3771.2381.0401.0081.304
20081.3541.2171.0220.9911.282
\n", - "
" - ], - "text/plain": [ - " OL Claim Ratio 2004 (19) OL Claim Ratio 2005 (20) OL Claim Ratio 2006 (21) OL Claim Ratio 2007 (22) OL Claim Ratio 2008 (23)\n", - "2002 0.758 0.682 0.573 0.555 0.718\n", - "2003 0.659 0.593 0.498 0.483 0.624\n", - "2004 0.782 0.703 0.591 0.573 0.740\n", - "2005 0.632 0.568 0.477 0.463 0.598\n", - "2006 0.803 0.722 0.606 0.588 0.760\n", - "2007 1.377 1.238 1.040 1.008 1.304\n", - "2008 1.354 1.217 1.022 0.991 1.282" - ] - }, - "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", - "
20042005200620072008
All Years (24)0.9090.8180.6870.6660.861
All Years ex Hi/Lo (24)0.8710.7830.6580.6380.825
Latest 5 (24)0.9890.8900.7470.7250.937
Latest 3 (24)1.1781.0590.8900.8631.115
Selected (25)0.8710.7830.6580.6380.825
\n", - "
" - ], - "text/plain": [ - " 2004 2005 2006 2007 2008\n", - "All Years (24) 0.909 0.818 0.687 0.666 0.861\n", - "All Years ex Hi/Lo (24) 0.871 0.783 0.658 0.638 0.825\n", - "Latest 5 (24) 0.989 0.890 0.747 0.725 0.937\n", - "Latest 3 (24) 1.178 1.059 0.890 0.863 1.115\n", - "Selected (25) 0.871 0.783 0.658 0.638 0.825" - ] - }, - "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", + "
Reported (2)Paid (3)Dev Ult Reported (4)Dev Ult Paid (5)Expected Claims (6)
Total449626.0330627.0514930.0605028.0561514.0
\n", + "
" ], - "source": [ - "def relative_level_triangle(base, years=range(2004, 2009)):\n", - " pieces = []\n", - " for year in years:\n", - " rel = base / base.loc[:, :, str(year), :]\n", - " rel.columns = [str(year)]\n", - " pieces.append(rel)\n", - " return cl.concat(pieces, axis=1)\n", - "\n", - "\n", - "selected_view = np.round(selected_ultimate.iloc[:, :, 4:, :], 0)\n", - "earned_premium_view = earned_premium.iloc[:, :, 4:, :]\n", - "\n", - "base_trend_2008 = (\n", - " cl.Trend(trends=[0.03425], dates=[(\"2008-12-31\", \"2002-01-01\")])\n", - " .fit(selected_view)\n", - " .trend_\n", - ")\n", - "severity_trend_adjustment = relative_level_triangle(base_trend_2008)\n", - "\n", - "base_tort_2008 = (\n", - " cl.Trend(\n", - " trends=[-0.25, 0.670 / 0.75 - 1],\n", - " dates=[(\"2007-12-31\", \"2006-12-31\"), (\"2006-12-31\", \"2005-12-31\")],\n", - " )\n", - " .fit(selected_view)\n", - " .trend_\n", - ")\n", - "tort_reform_adjustment = relative_level_triangle(base_tort_2008)\n", - "\n", - "rate_changes = [0, 0.05, 0.075, 0.15, 0.1, -0.2, -0.2]\n", - "olf = cl.parallelogram_olf(\n", - " rate_changes,\n", - " pd.to_datetime([f\"{y}-01-01\" for y in range(2002, 2009)]),\n", - " vertical_line=True,\n", - ")[\"OLF\"].values\n", - "base_rate = selected_view * 0 + olf.reshape(selected_view.shape)\n", - "rate_level_adjustment = relative_level_triangle(base_rate)\n", - "\n", - "adjusted_claim_ratios = (\n", - " selected_view * severity_trend_adjustment * tort_reform_adjustment\n", - ") / (earned_premium_view * rate_level_adjustment)\n", - "\n", - "ay = list(range(2002, 2009))\n", - "ty = list(range(2004, 2009))\n", - "\n", - "exhibit_iii_s2_ult = pd.DataFrame(\n", - " {\"Initial Selected Ult (2)\": as_series(selected_view).values}, index=ay\n", - ")\n", - "display(exhibit_iii_s2_ult)\n", - "\n", - "exhibit_iii_s2_trend = pd.DataFrame(\n", - " np.round(severity_trend_adjustment.values.squeeze().T, 3),\n", - " index=ay,\n", - " columns=[f\"Trend to {y} ({n})\" for y, n in zip(ty, [3, 4, 5, 6, 7])],\n", - ")\n", - "display(exhibit_iii_s2_trend)\n", - "\n", - "exhibit_iii_s2_tort = pd.DataFrame(\n", - " np.round(tort_reform_adjustment.values.squeeze().T, 3),\n", - " index=ay,\n", - " columns=[f\"Tort to {y} ({n})\" for y, n in zip(ty, [8, 9, 10, 11, 12])],\n", - ")\n", - "display(exhibit_iii_s2_tort)\n", - "\n", - "exhibit_iii_s2_prem = pd.DataFrame(\n", - " {\"Earned Premium (13)\": as_series(earned_premium_view).values}, index=ay\n", - ")\n", - "display(exhibit_iii_s2_prem)\n", - "\n", - "exhibit_iii_s2_rate = pd.DataFrame(\n", - " np.round(rate_level_adjustment.values.squeeze().T, 3),\n", - " index=ay,\n", - " columns=[f\"Rate to {y} ({n})\" for y, n in zip(ty, [14, 15, 16, 17, 18])],\n", - ")\n", - "display(exhibit_iii_s2_rate)\n", - "\n", - "exhibit_iii_s2_lr = pd.DataFrame(\n", - " np.round(adjusted_claim_ratios.values.squeeze().T, 3),\n", - " index=ay,\n", - " columns=[f\"OL Claim Ratio {y} ({n})\" for y, n in zip(ty, [19, 20, 21, 22, 23])],\n", - ")\n", - "display(exhibit_iii_s2_lr)\n", - "\n", - "vals = adjusted_claim_ratios.values.squeeze()\n", - "all_years = adjusted_claim_ratios.mean(axis=\"origin\").values.squeeze()\n", - "ex_high_low = (vals.sum(axis=1) - vals.max(axis=1) - vals.min(axis=1)) / 5\n", - "latest_5 = (\n", - " adjusted_claim_ratios.loc[:, :, \"2004\":, :].mean(axis=\"origin\").values.squeeze()\n", - ")\n", - "latest_3 = (\n", - " adjusted_claim_ratios.loc[:, :, \"2006\":, :].mean(axis=\"origin\").values.squeeze()\n", - ")\n", - "average_claim_ratios = np.vstack([all_years, ex_high_low, latest_5, latest_3])\n", - "selected_expected_claim_ratio = ex_high_low\n", - "\n", - "exhibit_iii_s2_avgs = pd.DataFrame(\n", - " np.round(average_claim_ratios, 3),\n", - " index=[\"All Years (24)\", \"All Years ex Hi/Lo (24)\", \"Latest 5 (24)\", \"Latest 3 (24)\"],\n", - " columns=ty,\n", - ")\n", - "exhibit_iii_s2_avgs.loc[\"Selected (25)\"] = np.round(selected_expected_claim_ratio, 3)\n", - "display(exhibit_iii_s2_avgs)\n" + "text/plain": [ + " Reported (2) Paid (3) Dev Ult Reported (4) Dev Ult Paid (5) Expected Claims (6)\n", + "Total 449626.0 330627.0 514930.0 605028.0 561514.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "exhibit_iii_s4 = pd.DataFrame(index=years)\n", + "exhibit_iii_s4[\"Reported (2)\"] = exhibit_iii_s1[\"Reported (2)\"]\n", + "exhibit_iii_s4[\"Paid (3)\"] = exhibit_iii_s1[\"Paid (3)\"]\n", + "exhibit_iii_s4[\"Dev Ult Reported (4)\"] = exhibit_iii_s1[\"Ult Reported (6)\"]\n", + "exhibit_iii_s4[\"Dev Ult Paid (5)\"] = exhibit_iii_s1[\"Ult Paid (7)\"]\n", + "exhibit_iii_s4[\"Expected Claims (6)\"] = exhibit_iii_s1[\"Expected Claims (12)\"]\n", + "display(exhibit_iii_s4)\n", + "display(exhibit_iii_s4.sum().rename(\"Total\").to_frame().T)" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "f5bea395", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.171408Z", + "iopub.status.busy": "2026-07-23T16:32:58.171271Z", + "iopub.status.idle": "2026-07-23T16:32:58.175488Z", + "shell.execute_reply": "2026-07-23T16:32:58.175003Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit III Sheet 4 — reconcile to Friedland PDF p147\n", + "assert np.allclose(\n", + " exhibit_iii_s4[\"Dev Ult Reported (4)\"],\n", + " [15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s4[\"Dev Ult Paid (5)\"],\n", + " [15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s4[\"Expected Claims (6)\"],\n", + " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_iii_s4[\"Dev Ult Reported (4)\"].sum(), 514929, atol=1)\n", + "assert np.isclose(exhibit_iii_s4[\"Dev Ult Paid (5)\"].sum(), 605030, atol=1)\n", + "assert np.isclose(exhibit_iii_s4[\"Expected Claims (6)\"].sum(), 561516, atol=1)" + ] + }, + { + "cell_type": "markdown", + "id": "1cbe699e", + "metadata": {}, + "source": [ + "## P148 (Exhibit III Sheet 5)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "64a93778", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.177559Z", + "iopub.status.busy": "2026-07-23T16:32:58.177401Z", + "iopub.status.idle": "2026-07-23T16:32:58.187652Z", + "shell.execute_reply": "2026-07-23T16:32:58.186954Z" + } + }, + "outputs": [ { - "cell_type": "code", - "execution_count": 13, - "id": "9c4d3bd9", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.114731Z", - "iopub.status.busy": "2026-07-23T16:32:58.114586Z", - "iopub.status.idle": "2026-07-23T16:32:58.122532Z", - "shell.execute_reply": "2026-07-23T16:32:58.121977Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit III Sheet 2 — reconcile to Friedland PDF p145\n", - "assert np.allclose(\n", - " as_series(selected_view).values,\n", - " [48953, 47404, 77662, 78497, 65239, 62960, 61262],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s2_trend.values,\n", - " [\n", - " [1.070, 1.106, 1.144, 1.183, 1.224],\n", - " [1.034, 1.070, 1.106, 1.144, 1.183],\n", - " [1.000, 1.034, 1.070, 1.106, 1.144],\n", - " [0.967, 1.000, 1.034, 1.070, 1.106],\n", - " [0.935, 0.967, 1.000, 1.034, 1.070],\n", - " [0.904, 0.935, 0.967, 1.000, 1.034],\n", - " [0.874, 0.904, 0.935, 0.967, 1.000],\n", - " ],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s2_tort.values,\n", - " [\n", - " [1.000, 1.000, 0.893, 0.670, 0.670],\n", - " [1.000, 1.000, 0.893, 0.670, 0.670],\n", - " [1.000, 1.000, 0.893, 0.670, 0.670],\n", - " [1.000, 1.000, 0.893, 0.670, 0.670],\n", - " [1.119, 1.119, 1.000, 0.750, 0.750],\n", - " [1.493, 1.493, 1.333, 1.000, 1.000],\n", - " [1.493, 1.493, 1.333, 1.000, 1.000],\n", - " ],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s2_prem[\"Earned Premium (13)\"],\n", - " [61183, 69175, 99322, 138151, 107578, 62438, 47797],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s2_rate.values,\n", - " [\n", - " [1.129, 1.298, 1.428, 1.142, 0.914],\n", - " [1.075, 1.236, 1.360, 1.088, 0.870],\n", - " [1.000, 1.150, 1.265, 1.012, 0.810],\n", - " [0.870, 1.000, 1.100, 0.880, 0.704],\n", - " [0.791, 0.909, 1.000, 0.800, 0.640],\n", - " [0.988, 1.136, 1.250, 1.000, 0.800],\n", - " [1.235, 1.420, 1.562, 1.250, 1.000],\n", - " ],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s2_lr.values,\n", - " [\n", - " [0.758, 0.682, 0.573, 0.555, 0.718],\n", - " [0.659, 0.593, 0.498, 0.483, 0.624],\n", - " [0.782, 0.703, 0.591, 0.573, 0.740],\n", - " [0.632, 0.568, 0.477, 0.463, 0.598],\n", - " [0.803, 0.722, 0.606, 0.588, 0.760],\n", - " [1.377, 1.238, 1.040, 1.008, 1.304],\n", - " [1.354, 1.217, 1.022, 0.991, 1.282],\n", - " ],\n", - ")\n", - "assert np.allclose(\n", - " np.round(average_claim_ratios, 3),\n", - " [\n", - " [0.909, 0.818, 0.687, 0.666, 0.861],\n", - " [0.871, 0.783, 0.658, 0.638, 0.825],\n", - " [0.989, 0.890, 0.747, 0.725, 0.937],\n", - " [1.178, 1.059, 0.890, 0.863, 1.115],\n", - " ],\n", - ")\n", - "assert np.allclose(\n", - " np.round(selected_expected_claim_ratio, 3), [0.871, 0.783, 0.658, 0.638, 0.825]\n", - ")\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", + "
Case Outstanding (2)Dev IBNR Reported (3)Dev IBNR Paid (4)Expected IBNR (5)
19980.00.0158.0-162.0
1999290.0-25.057.0-443.0
2000464.0-298.0676.0-2011.0
2001279.0-310.01801.0352.0
20023732.0145.01423.0-263.0
20035053.0577.05485.09791.0
200417477.04498.010249.016221.0
200530629.06006.09677.037517.0
200625985.09566.023304.021982.0
200719867.016247.046209.08103.0
200815223.028898.056363.020801.0
\n", + "
" + ], + "text/plain": [ + " Case Outstanding (2) Dev IBNR Reported (3) Dev IBNR Paid (4) Expected IBNR (5)\n", + "1998 0.0 0.0 158.0 -162.0\n", + "1999 290.0 -25.0 57.0 -443.0\n", + "2000 464.0 -298.0 676.0 -2011.0\n", + "2001 279.0 -310.0 1801.0 352.0\n", + "2002 3732.0 145.0 1423.0 -263.0\n", + "2003 5053.0 577.0 5485.0 9791.0\n", + "2004 17477.0 4498.0 10249.0 16221.0\n", + "2005 30629.0 6006.0 9677.0 37517.0\n", + "2006 25985.0 9566.0 23304.0 21982.0\n", + "2007 19867.0 16247.0 46209.0 8103.0\n", + "2008 15223.0 28898.0 56363.0 20801.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "e42bf89a", - "metadata": {}, - "source": [ - "## P146 (Exhibit III Sheet 3)\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", + "
Case Outstanding (2)Dev IBNR Reported (3)Dev IBNR Paid (4)Expected IBNR (5)
Total118999.065304.0155402.0111888.0
\n", + "
" + ], + "text/plain": [ + " Case Outstanding (2) Dev IBNR Reported (3) Dev IBNR Paid (4) Expected IBNR (5)\n", + "Total 118999.0 65304.0 155402.0 111888.0" ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "exhibit_iii_s5 = pd.DataFrame(index=years)\n", + "exhibit_iii_s5[\"Case Outstanding (2)\"] = exhibit_iii_s3[\"Case Outstanding (5)\"]\n", + "exhibit_iii_s5[\"Dev IBNR Reported (3)\"] = (\n", + " exhibit_iii_s4[\"Dev Ult Reported (4)\"] - exhibit_iii_s4[\"Reported (2)\"]\n", + ")\n", + "exhibit_iii_s5[\"Dev IBNR Paid (4)\"] = (\n", + " exhibit_iii_s4[\"Dev Ult Paid (5)\"] - exhibit_iii_s4[\"Reported (2)\"]\n", + ")\n", + "exhibit_iii_s5[\"Expected IBNR (5)\"] = exhibit_iii_s3[\"IBNR (6)\"]\n", + "display(exhibit_iii_s5)\n", + "display(exhibit_iii_s5.sum().rename(\"Total\").to_frame().T)" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "6bfe7ded", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.190974Z", + "iopub.status.busy": "2026-07-23T16:32:58.190055Z", + "iopub.status.idle": "2026-07-23T16:32:58.198600Z", + "shell.execute_reply": "2026-07-23T16:32:58.196859Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit III Sheet 5 — reconcile to Friedland PDF p148\n", + "assert np.allclose(\n", + " exhibit_iii_s5[\"Dev IBNR Reported (3)\"],\n", + " [0, -25, -298, -311, 144, 577, 4499, 6006, 9566, 16247, 28898],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s5[\"Dev IBNR Paid (4)\"],\n", + " [158, 58, 676, 1802, 1423, 5485, 10249, 9678, 23304, 46209, 56363],\n", + " atol=1,\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iii_s5[\"Expected IBNR (5)\"],\n", + " [-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_iii_s5[\"Case Outstanding (2)\"].sum(), 118997, atol=1)\n", + "assert np.isclose(exhibit_iii_s5[\"Dev IBNR Reported (3)\"].sum(), 65303, atol=1)\n", + "assert np.isclose(exhibit_iii_s5[\"Dev IBNR Paid (4)\"].sum(), 155405, atol=3)\n", + "assert np.isclose(exhibit_iii_s5[\"Expected IBNR (5)\"].sum(), 111890, atol=1)" + ] + }, + { + "cell_type": "markdown", + "id": "1aa6cc74", + "metadata": {}, + "source": [ + "## P149 (Exhibit IV Sheet 1)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "42abf044", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.204196Z", + "iopub.status.busy": "2026-07-23T16:32:58.203877Z", + "iopub.status.idle": "2026-07-23T16:32:58.335729Z", + "shell.execute_reply": "2026-07-23T16:32:58.334928Z" + } + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Steady-State\n" + ] }, { - "cell_type": "code", - "execution_count": 14, - "id": "b3874bf4", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.126283Z", - "iopub.status.busy": "2026-07-23T16:32:58.126065Z", - "iopub.status.idle": "2026-07-23T16:32:58.145115Z", - "shell.execute_reply": "2026-07-23T16:32:58.144576Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
199815822.015822.015660.00.0-162.0-162.0
199925107.024817.024664.0290.0-443.0-153.0
200037246.036782.035235.0464.0-2011.0-1547.0
200138798.038519.039150.0279.0352.0631.0
200248169.044437.047906.03732.0-263.03469.0
200344373.039320.054164.05053.09791.014844.0
200470288.052811.086509.017477.016221.033698.0
200570655.040026.0108172.030629.037517.068146.0
200648804.022819.070786.025985.021982.047967.0
200731732.011865.039835.019867.08103.027970.0
200818632.03409.039433.015223.020801.036024.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", - "1998 15822.0 15822.0 15660.0 0.0 -162.0 -162.0\n", - "1999 25107.0 24817.0 24664.0 290.0 -443.0 -153.0\n", - "2000 37246.0 36782.0 35235.0 464.0 -2011.0 -1547.0\n", - "2001 38798.0 38519.0 39150.0 279.0 352.0 631.0\n", - "2002 48169.0 44437.0 47906.0 3732.0 -263.0 3469.0\n", - "2003 44373.0 39320.0 54164.0 5053.0 9791.0 14844.0\n", - "2004 70288.0 52811.0 86509.0 17477.0 16221.0 33698.0\n", - "2005 70655.0 40026.0 108172.0 30629.0 37517.0 68146.0\n", - "2006 48804.0 22819.0 70786.0 25985.0 21982.0 47967.0\n", - "2007 31732.0 11865.0 39835.0 19867.0 8103.0 27970.0\n", - "2008 18632.0 3409.0 39433.0 15223.0 20801.0 36024.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", - "
Reported (2)Paid (3)Expected Claims (4)Case Outstanding (5)IBNR (6)Total Unpaid (7)
Total449626.0330627.0561514.0118999.0111888.0230887.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Expected Claims (4) Case Outstanding (5) IBNR (6) Total Unpaid (7)\n", - "Total 449626.0 330627.0 561514.0 118999.0 111888.0 230887.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08508.00.0
20041276282.00.7893397.0884463.08934.08934.00.0
20051340096.00.7938067.0919306.018761.018761.00.0
20061407100.00.7984970.0935722.049248.049249.01.0
20071477455.00.71034218.0930797.0103421.0103422.01.0
20081551328.00.71085930.0836166.0249764.0249764.00.0
\n", + "
" ], - "source": [ - "exhibit_iii_s3 = unpaid_exhibit(\n", - " xyz[\"Reported Claims\"], xyz[\"Paid Claims\"], xyz_expected_claims\n", - ")\n", - "display(exhibit_iii_s3)\n", - "display(exhibit_iii_s3.sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", + "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", + "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", + "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", + "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8508.0 0.0\n", + "2004 1276282.0 0.7 893397.0 884463.0 8934.0 8934.0 0.0\n", + "2005 1340096.0 0.7 938067.0 919306.0 18761.0 18761.0 0.0\n", + "2006 1407100.0 0.7 984970.0 935722.0 49248.0 49249.0 1.0\n", + "2007 1477455.0 0.7 1034218.0 930797.0 103421.0 103422.0 1.0\n", + "2008 1551328.0 0.7 1085930.0 836166.0 249764.0 249764.0 0.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 15, - "id": "27a8fcc2", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.150010Z", - "iopub.status.busy": "2026-07-23T16:32:58.149668Z", - "iopub.status.idle": "2026-07-23T16:32:58.154711Z", - "shell.execute_reply": "2026-07-23T16:32:58.154309Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit III Sheet 3 — reconcile to Friedland PDF p146\n", - "assert np.allclose(\n", - " exhibit_iii_s3[\"Expected Claims (4)\"],\n", - " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s3[\"Case Outstanding (5)\"],\n", - " [0, 290, 465, 278, 3731, 5052, 17477, 30629, 25985, 19867, 15223],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s3[\"IBNR (6)\"],\n", - " [-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s3[\"Total Unpaid (7)\"],\n", - " [-162, -152, -1547, 631, 3469, 14844, 33698, 68146, 47967, 27970, 36024],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_iii_s3[\"Reported (2)\"].sum(), 449626)\n", - "assert np.isclose(exhibit_iii_s3[\"Paid (3)\"].sum(), 330629)\n", - "assert np.isclose(exhibit_iii_s3[\"Expected Claims (4)\"].sum(), 561516, atol=1)\n", - "assert np.isclose(exhibit_iii_s3[\"Case Outstanding (5)\"].sum(), 118997, atol=1)\n", - "assert np.isclose(exhibit_iii_s3[\"IBNR (6)\"].sum(), 111890, atol=1)\n", - "assert np.isclose(exhibit_iii_s3[\"Total Unpaid (7)\"].sum(), 230887, atol=1)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.08365888.0438636.0438638.02.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 12577892.0 8804524.0 8365888.0 438636.0 438638.0 2.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "6f43d18f", - "metadata": {}, - "source": [ - "## P147 (Exhibit III Sheet 4)\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Claim Ratios\n" + ] }, { - "cell_type": "code", - "execution_count": 16, - "id": "d2eeae83", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.157170Z", - "iopub.status.busy": "2026-07-23T16:32:58.156968Z", - "iopub.status.idle": "2026-07-23T16:32:58.168591Z", - "shell.execute_reply": "2026-07-23T16:32:58.168148Z" - } - }, - "outputs": [ - { - "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", - "
Reported (2)Paid (3)Dev Ult Reported (4)Dev Ult Paid (5)Expected Claims (6)
199815822.015822.015822.015980.015660.0
199925107.024817.025082.025164.024664.0
200037246.036782.036948.037922.035235.0
200138798.038519.038488.040599.039150.0
200248169.044437.048314.049592.047906.0
200344373.039320.044950.049858.054164.0
200470288.052811.074786.080537.086509.0
200570655.040026.076661.080332.0108172.0
200648804.022819.058370.072108.070786.0
200731732.011865.047979.077941.039835.0
200818632.03409.047530.074995.039433.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Dev Ult Reported (4) Dev Ult Paid (5) Expected Claims (6)\n", - "1998 15822.0 15822.0 15822.0 15980.0 15660.0\n", - "1999 25107.0 24817.0 25082.0 25164.0 24664.0\n", - "2000 37246.0 36782.0 36948.0 37922.0 35235.0\n", - "2001 38798.0 38519.0 38488.0 40599.0 39150.0\n", - "2002 48169.0 44437.0 48314.0 49592.0 47906.0\n", - "2003 44373.0 39320.0 44950.0 49858.0 54164.0\n", - "2004 70288.0 52811.0 74786.0 80537.0 86509.0\n", - "2005 70655.0 40026.0 76661.0 80332.0 108172.0\n", - "2006 48804.0 22819.0 58370.0 72108.0 70786.0\n", - "2007 31732.0 11865.0 47979.0 77941.0 39835.0\n", - "2008 18632.0 3409.0 47530.0 74995.0 39433.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", - "
Reported (2)Paid (3)Dev Ult Reported (4)Dev Ult Paid (5)Expected Claims (6)
Total449626.0330627.0514930.0605028.0561514.0
\n", - "
" - ], - "text/plain": [ - " Reported (2) Paid (3) Dev Ult Reported (4) Dev Ult Paid (5) Expected Claims (6)\n", - "Total 449626.0 330627.0 514930.0 605028.0 561514.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08508.00.0
20041276282.00.7893397.01010815.0-117418.010210.0127628.0
20051340096.00.7938067.01116300.0-178233.022782.0201015.0
20061407100.00.7984970.01203071.0-218101.063320.0281421.0
20071477455.00.71034218.01263224.0-229006.0140358.0369364.0
20081551328.00.71085930.01194523.0-108593.0356805.0465398.0
\n", + "
" ], - "source": [ - "exhibit_iii_s4 = pd.DataFrame(index=years)\n", - "exhibit_iii_s4[\"Reported (2)\"] = exhibit_iii_s1[\"Reported (2)\"]\n", - "exhibit_iii_s4[\"Paid (3)\"] = exhibit_iii_s1[\"Paid (3)\"]\n", - "exhibit_iii_s4[\"Dev Ult Reported (4)\"] = exhibit_iii_s1[\"Ult Reported (6)\"]\n", - "exhibit_iii_s4[\"Dev Ult Paid (5)\"] = exhibit_iii_s1[\"Ult Paid (7)\"]\n", - "exhibit_iii_s4[\"Expected Claims (6)\"] = exhibit_iii_s1[\"Expected Claims (12)\"]\n", - "display(exhibit_iii_s4)\n", - "display(exhibit_iii_s4.sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", + "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", + "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", + "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", + "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8508.0 0.0\n", + "2004 1276282.0 0.7 893397.0 1010815.0 -117418.0 10210.0 127628.0\n", + "2005 1340096.0 0.7 938067.0 1116300.0 -178233.0 22782.0 201015.0\n", + "2006 1407100.0 0.7 984970.0 1203071.0 -218101.0 63320.0 281421.0\n", + "2007 1477455.0 0.7 1034218.0 1263224.0 -229006.0 140358.0 369364.0\n", + "2008 1551328.0 0.7 1085930.0 1194523.0 -108593.0 356805.0 465398.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 17, - "id": "f5bea395", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.171408Z", - "iopub.status.busy": "2026-07-23T16:32:58.171271Z", - "iopub.status.idle": "2026-07-23T16:32:58.175488Z", - "shell.execute_reply": "2026-07-23T16:32:58.175003Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit III Sheet 4 — reconcile to Friedland PDF p147\n", - "assert np.allclose(\n", - " exhibit_iii_s4[\"Dev Ult Reported (4)\"],\n", - " [15822, 25082, 36948, 38487, 48313, 44950, 74787, 76661, 58370, 47979, 47530],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s4[\"Dev Ult Paid (5)\"],\n", - " [15980, 25164, 37922, 40600, 49592, 49858, 80537, 80333, 72108, 77941, 74995],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s4[\"Expected Claims (6)\"],\n", - " [15660, 24665, 35235, 39150, 47906, 54164, 86509, 108172, 70786, 39835, 39433],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_iii_s4[\"Dev Ult Reported (4)\"].sum(), 514929, atol=1)\n", - "assert np.isclose(exhibit_iii_s4[\"Dev Ult Paid (5)\"].sum(), 605030, atol=1)\n", - "assert np.isclose(exhibit_iii_s4[\"Expected Claims (6)\"].sum(), 561516, atol=1)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.09647367.0-842843.0601983.01444826.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 12577892.0 8804524.0 9647367.0 -842843.0 601983.0 1444826.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "def changing_conditions_exhibit(triangle, claim_ratio, actual_ibnr_values):\n", + " earned = np.round(triangle[\"Earned Premium\"].latest_diagonal, 0)\n", + " el = cl.ExpectedLoss(apriori=claim_ratio).fit(\n", + " triangle[\"Reported Claims\"], sample_weight=earned\n", + " )\n", + " expected = np.round(el.ultimate_, 0)\n", + " reported = triangle[\"Reported Claims\"].latest_diagonal\n", + " estimated_ibnr = np.round(el.ibnr_, 0).fillzero()\n", + " actual_ibnr = estimated_ibnr.copy()\n", + " actual_ibnr.values = np.array(actual_ibnr_values, dtype=float).reshape(\n", + " estimated_ibnr.shape\n", + " )\n", + " difference = np.round(actual_ibnr - estimated_ibnr, 0).fillzero()\n", + " out = pd.DataFrame(index=list(triangle[\"Reported Claims\"].origin.year))\n", + " out[\"Earned Premium (2)\"] = as_series(earned).values\n", + " out[\"Claim Ratio (3)\"] = claim_ratio\n", + " out[\"Expected Claims (4)\"] = as_series(expected).values\n", + " out[\"Reported (5)\"] = as_series(reported).values\n", + " out[\"Estimated IBNR (6)\"] = as_series(estimated_ibnr).values\n", + " out[\"Actual IBNR (7)\"] = as_series(actual_ibnr).values\n", + " out[\"Difference (8)\"] = as_series(difference).values\n", + " return out\n", + "\n", + "\n", + "uspp = cl.load_sample(\"friedland_uspp\")\n", + "\n", + "exhibit_iv_steady = changing_conditions_exhibit(\n", + " uspp.loc[\"Steady State\"],\n", + " 0.70,\n", + " [0, 0, 0, 0, 8508, 8934, 18761, 49249, 103422, 249764],\n", + ")\n", + "exhibit_iv_incr_claim = changing_conditions_exhibit(\n", + " uspp.loc[\"Increasing Claim\"],\n", + " 0.70,\n", + " [0, 0, 0, 0, 8508, 10210, 22782, 63320, 140358, 356805],\n", + ")\n", + "\n", + "print(\"Steady-State\")\n", + "display(exhibit_iv_steady)\n", + "display(\n", + " exhibit_iv_steady[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")\n", + "\n", + "print(\"Increasing Claim Ratios\")\n", + "display(exhibit_iv_incr_claim)\n", + "display(\n", + " exhibit_iv_incr_claim[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "75d0449f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.342004Z", + "iopub.status.busy": "2026-07-23T16:32:58.341713Z", + "iopub.status.idle": "2026-07-23T16:32:58.348054Z", + "shell.execute_reply": "2026-07-23T16:32:58.347393Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit IV Sheet 1 — reconcile to Friedland PDF p149\n", + "assert np.allclose(\n", + " exhibit_iv_steady[\"Earned Premium (2)\"],\n", + " [\n", + " 1000000,\n", + " 1050000,\n", + " 1102500,\n", + " 1157625,\n", + " 1215506,\n", + " 1276282,\n", + " 1340096,\n", + " 1407100,\n", + " 1477455,\n", + " 1551328,\n", + " ],\n", + ")\n", + "assert np.allclose(\n", + " exhibit_iv_steady[\"Expected Claims (4)\"],\n", + " [700000, 735000, 771750, 810338, 850854, 893397, 938067, 984970, 1034219, 1085930],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_iv_steady[\"Earned Premium (2)\"].sum(), 12577893, atol=1)\n", + "assert np.isclose(exhibit_iv_steady[\"Expected Claims (4)\"].sum(), 8804525, atol=1)\n", + "assert np.isclose(exhibit_iv_steady[\"Estimated IBNR (6)\"].sum(), 438638, atol=2)\n", + "assert np.isclose(exhibit_iv_steady[\"Difference (8)\"].sum(), 0, atol=2)\n", + "\n", + "assert np.allclose(\n", + " exhibit_iv_incr_claim[\"Estimated IBNR (6)\"],\n", + " [0, 0, 0, 0, 8508, -117418, -178233, -218101, -229006, -108593],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_iv_incr_claim[\"Estimated IBNR (6)\"].sum(), -842841, atol=2)\n", + "assert np.isclose(exhibit_iv_incr_claim[\"Actual IBNR (7)\"].sum(), 601984, atol=1)\n", + "assert np.isclose(exhibit_iv_incr_claim[\"Difference (8)\"].sum(), 1444824, atol=2)" + ] + }, + { + "cell_type": "markdown", + "id": "0e80110a", + "metadata": {}, + "source": [ + "## P150 (Exhibit IV Sheet 2)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "480249f8", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.352619Z", + "iopub.status.busy": "2026-07-23T16:32:58.352453Z", + "iopub.status.idle": "2026-07-23T16:32:58.471842Z", + "shell.execute_reply": "2026-07-23T16:32:58.471445Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "1cbe699e", - "metadata": {}, - "source": [ - "## P148 (Exhibit III Sheet 5)\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Case Outstanding Strength\n" + ] }, { - "cell_type": "code", - "execution_count": 18, - "id": "64a93778", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.177559Z", - "iopub.status.busy": "2026-07-23T16:32:58.177401Z", - "iopub.status.idle": "2026-07-23T16:32:58.187652Z", - "shell.execute_reply": "2026-07-23T16:32:58.186954Z" - } - }, - "outputs": [ - { - "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", - "
Case Outstanding (2)Dev IBNR Reported (3)Dev IBNR Paid (4)Expected IBNR (5)
19980.00.0158.0-162.0
1999290.0-25.057.0-443.0
2000464.0-298.0676.0-2011.0
2001279.0-310.01801.0352.0
20023732.0145.01423.0-263.0
20035053.0577.05485.09791.0
200417477.04498.010249.016221.0
200530629.06006.09677.037517.0
200625985.09566.023304.021982.0
200719867.016247.046209.08103.0
200815223.028898.056363.020801.0
\n", - "
" - ], - "text/plain": [ - " Case Outstanding (2) Dev IBNR Reported (3) Dev IBNR Paid (4) Expected IBNR (5)\n", - "1998 0.0 0.0 158.0 -162.0\n", - "1999 290.0 -25.0 57.0 -443.0\n", - "2000 464.0 -298.0 676.0 -2011.0\n", - "2001 279.0 -310.0 1801.0 352.0\n", - "2002 3732.0 145.0 1423.0 -263.0\n", - "2003 5053.0 577.0 5485.0 9791.0\n", - "2004 17477.0 4498.0 10249.0 16221.0\n", - "2005 30629.0 6006.0 9677.0 37517.0\n", - "2006 25985.0 9566.0 23304.0 21982.0\n", - "2007 19867.0 16247.0 46209.0 8103.0\n", - "2008 15223.0 28898.0 56363.0 20801.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", - "
Case Outstanding (2)Dev IBNR Reported (3)Dev IBNR Paid (4)Expected IBNR (5)
Total118999.065304.0155402.0111888.0
\n", - "
" - ], - "text/plain": [ - " Case Outstanding (2) Dev IBNR Reported (3) Dev IBNR Paid (4) Expected IBNR (5)\n", - "Total 118999.0 65304.0 155402.0 111888.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08509.01.0
20041276282.00.7893397.0884463.08934.08934.00.0
20051340096.00.7938067.0933377.04690.04690.00.0
20061407100.00.7984970.0962808.022162.022162.00.0
20071477455.00.71034218.0979922.054296.054296.00.0
20081551328.00.71085930.0931185.0154745.0154745.00.0
\n", + "
" ], - "source": [ - "exhibit_iii_s5 = pd.DataFrame(index=years)\n", - "exhibit_iii_s5[\"Case Outstanding (2)\"] = exhibit_iii_s3[\"Case Outstanding (5)\"]\n", - "exhibit_iii_s5[\"Dev IBNR Reported (3)\"] = (\n", - " exhibit_iii_s4[\"Dev Ult Reported (4)\"] - exhibit_iii_s4[\"Reported (2)\"]\n", - ")\n", - "exhibit_iii_s5[\"Dev IBNR Paid (4)\"] = (\n", - " exhibit_iii_s4[\"Dev Ult Paid (5)\"] - exhibit_iii_s4[\"Reported (2)\"]\n", - ")\n", - "exhibit_iii_s5[\"Expected IBNR (5)\"] = exhibit_iii_s3[\"IBNR (6)\"]\n", - "display(exhibit_iii_s5)\n", - "display(exhibit_iii_s5.sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", + "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", + "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", + "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", + "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8509.0 1.0\n", + "2004 1276282.0 0.7 893397.0 884463.0 8934.0 8934.0 0.0\n", + "2005 1340096.0 0.7 938067.0 933377.0 4690.0 4690.0 0.0\n", + "2006 1407100.0 0.7 984970.0 962808.0 22162.0 22162.0 0.0\n", + "2007 1477455.0 0.7 1034218.0 979922.0 54296.0 54296.0 0.0\n", + "2008 1551328.0 0.7 1085930.0 931185.0 154745.0 154745.0 0.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 19, - "id": "6bfe7ded", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.190974Z", - "iopub.status.busy": "2026-07-23T16:32:58.190055Z", - "iopub.status.idle": "2026-07-23T16:32:58.198600Z", - "shell.execute_reply": "2026-07-23T16:32:58.196859Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit III Sheet 5 — reconcile to Friedland PDF p148\n", - "assert np.allclose(\n", - " exhibit_iii_s5[\"Dev IBNR Reported (3)\"],\n", - " [0, -25, -298, -311, 144, 577, 4499, 6006, 9566, 16247, 28898],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s5[\"Dev IBNR Paid (4)\"],\n", - " [158, 58, 676, 1802, 1423, 5485, 10249, 9678, 23304, 46209, 56363],\n", - " atol=1,\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iii_s5[\"Expected IBNR (5)\"],\n", - " [-162, -442, -2011, 352, -262, 9791, 16221, 37517, 21982, 8103, 20801],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_iii_s5[\"Case Outstanding (2)\"].sum(), 118997, atol=1)\n", - "assert np.isclose(exhibit_iii_s5[\"Dev IBNR Reported (3)\"].sum(), 65303, atol=1)\n", - "assert np.isclose(exhibit_iii_s5[\"Dev IBNR Paid (4)\"].sum(), 155405, atol=3)\n", - "assert np.isclose(exhibit_iii_s5[\"Expected IBNR (5)\"].sum(), 111890, atol=1)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.08551189.0253335.0253336.01.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 12577892.0 8804524.0 8551189.0 253335.0 253336.0 1.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "1aa6cc74", - "metadata": {}, - "source": [ - "## P149 (Exhibit IV Sheet 1)\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Increasing Claim Ratios and Case Outstanding Strength\n" + ] }, { - "cell_type": "code", - "execution_count": 20, - "id": "42abf044", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.204196Z", - "iopub.status.busy": "2026-07-23T16:32:58.203877Z", - "iopub.status.idle": "2026-07-23T16:32:58.335729Z", - "shell.execute_reply": "2026-07-23T16:32:58.334928Z" - } - }, - "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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08508.00.0
20041276282.00.7893397.0884463.08934.08934.00.0
20051340096.00.7938067.0919306.018761.018761.00.0
20061407100.00.7984970.0935722.049248.049249.01.0
20071477455.00.71034218.0930797.0103421.0103422.01.0
20081551328.00.71085930.0836166.0249764.0249764.00.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", - "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", - "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", - "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", - "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8508.0 0.0\n", - "2004 1276282.0 0.7 893397.0 884463.0 8934.0 8934.0 0.0\n", - "2005 1340096.0 0.7 938067.0 919306.0 18761.0 18761.0 0.0\n", - "2006 1407100.0 0.7 984970.0 935722.0 49248.0 49249.0 1.0\n", - "2007 1477455.0 0.7 1034218.0 930797.0 103421.0 103422.0 1.0\n", - "2008 1551328.0 0.7 1085930.0 836166.0 249764.0 249764.0 0.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.08365888.0438636.0438638.02.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 12577892.0 8804524.0 8365888.0 438636.0 438638.0 2.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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08508.00.0
20041276282.00.7893397.01010815.0-117418.010210.0127628.0
20051340096.00.7938067.01116300.0-178233.022782.0201015.0
20061407100.00.7984970.01203071.0-218101.063320.0281421.0
20071477455.00.71034218.01263224.0-229006.0140358.0369364.0
20081551328.00.71085930.01194523.0-108593.0356805.0465398.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", - "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", - "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", - "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", - "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8508.0 0.0\n", - "2004 1276282.0 0.7 893397.0 1010815.0 -117418.0 10210.0 127628.0\n", - "2005 1340096.0 0.7 938067.0 1116300.0 -178233.0 22782.0 201015.0\n", - "2006 1407100.0 0.7 984970.0 1203071.0 -218101.0 63320.0 281421.0\n", - "2007 1477455.0 0.7 1034218.0 1263224.0 -229006.0 140358.0 369364.0\n", - "2008 1551328.0 0.7 1085930.0 1194523.0 -108593.0 356805.0 465398.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.09647367.0-842843.0601983.01444826.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 12577892.0 8804524.0 9647367.0 -842843.0 601983.0 1444826.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08509.01.0
20041276282.00.7893397.01010815.0-117418.010210.0127628.0
20051340096.00.7938067.01133386.0-195319.05695.0201014.0
20061407100.00.7984970.01237897.0-252927.028494.0281421.0
20071477455.00.71034218.01329895.0-295677.073688.0369365.0
20081551328.00.71085930.01330264.0-244334.0221064.0465398.0
\n", + "
" ], - "source": [ - "def changing_conditions_exhibit(triangle, claim_ratio, actual_ibnr_values):\n", - " earned = np.round(triangle[\"Earned Premium\"].latest_diagonal, 0)\n", - " el = cl.ExpectedLoss(apriori=claim_ratio).fit(\n", - " triangle[\"Reported Claims\"], sample_weight=earned\n", - " )\n", - " expected = np.round(el.ultimate_, 0)\n", - " reported = triangle[\"Reported Claims\"].latest_diagonal\n", - " estimated_ibnr = np.round(el.ibnr_, 0).fillzero()\n", - " actual_ibnr = estimated_ibnr.copy()\n", - " actual_ibnr.values = np.array(actual_ibnr_values, dtype=float).reshape(\n", - " estimated_ibnr.shape\n", - " )\n", - " difference = np.round(actual_ibnr - estimated_ibnr, 0).fillzero()\n", - " out = pd.DataFrame(index=list(triangle[\"Reported Claims\"].origin.year))\n", - " out[\"Earned Premium (2)\"] = as_series(earned).values\n", - " out[\"Claim Ratio (3)\"] = claim_ratio\n", - " out[\"Expected Claims (4)\"] = as_series(expected).values\n", - " out[\"Reported (5)\"] = as_series(reported).values\n", - " out[\"Estimated IBNR (6)\"] = as_series(estimated_ibnr).values\n", - " out[\"Actual IBNR (7)\"] = as_series(actual_ibnr).values\n", - " out[\"Difference (8)\"] = as_series(difference).values\n", - " return out\n", - "\n", - "\n", - "uspp = cl.load_sample(\"friedland_uspp\")\n", - "\n", - "exhibit_iv_steady = changing_conditions_exhibit(\n", - " uspp.loc[\"Steady State\"],\n", - " 0.70,\n", - " [0, 0, 0, 0, 8508, 8934, 18761, 49249, 103422, 249764],\n", - ")\n", - "exhibit_iv_incr_claim = changing_conditions_exhibit(\n", - " uspp.loc[\"Increasing Claim\"],\n", - " 0.70,\n", - " [0, 0, 0, 0, 8508, 10210, 22782, 63320, 140358, 356805],\n", - ")\n", - "\n", - "print(\"Steady-State\")\n", - "display(exhibit_iv_steady)\n", - "display(exhibit_iv_steady[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n", - "\n", - "print(\"Increasing Claim Ratios\")\n", - "display(exhibit_iv_incr_claim)\n", - "display(exhibit_iv_incr_claim[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", + "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", + "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", + "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", + "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8509.0 1.0\n", + "2004 1276282.0 0.7 893397.0 1010815.0 -117418.0 10210.0 127628.0\n", + "2005 1340096.0 0.7 938067.0 1133386.0 -195319.0 5695.0 201014.0\n", + "2006 1407100.0 0.7 984970.0 1237897.0 -252927.0 28494.0 281421.0\n", + "2007 1477455.0 0.7 1034218.0 1329895.0 -295677.0 73688.0 369365.0\n", + "2008 1551328.0 0.7 1085930.0 1330264.0 -244334.0 221064.0 465398.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 21, - "id": "75d0449f", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.342004Z", - "iopub.status.busy": "2026-07-23T16:32:58.341713Z", - "iopub.status.idle": "2026-07-23T16:32:58.348054Z", - "shell.execute_reply": "2026-07-23T16:32:58.347393Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit IV Sheet 1 — reconcile to Friedland PDF p149\n", - "assert np.allclose(\n", - " exhibit_iv_steady[\"Earned Premium (2)\"],\n", - " [1000000, 1050000, 1102500, 1157625, 1215506, 1276282, 1340096, 1407100, 1477455, 1551328],\n", - ")\n", - "assert np.allclose(\n", - " exhibit_iv_steady[\"Expected Claims (4)\"],\n", - " [700000, 735000, 771750, 810338, 850854, 893397, 938067, 984970, 1034219, 1085930],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_iv_steady[\"Earned Premium (2)\"].sum(), 12577893, atol=1)\n", - "assert np.isclose(exhibit_iv_steady[\"Expected Claims (4)\"].sum(), 8804525, atol=1)\n", - "assert np.isclose(exhibit_iv_steady[\"Estimated IBNR (6)\"].sum(), 438638, atol=2)\n", - "assert np.isclose(exhibit_iv_steady[\"Difference (8)\"].sum(), 0, atol=2)\n", - "\n", - "assert np.allclose(\n", - " exhibit_iv_incr_claim[\"Estimated IBNR (6)\"],\n", - " [0, 0, 0, 0, 8508, -117418, -178233, -218101, -229006, -108593],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_iv_incr_claim[\"Estimated IBNR (6)\"].sum(), -842841, atol=2)\n", - "assert np.isclose(exhibit_iv_incr_claim[\"Actual IBNR (7)\"].sum(), 601984, atol=1)\n", - "assert np.isclose(exhibit_iv_incr_claim[\"Difference (8)\"].sum(), 1444824, atol=2)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.09901691.0-1097167.0347660.01444827.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 12577892.0 8804524.0 9901691.0 -1097167.0 347660.0 1444827.0" ] - }, + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "exhibit_iv_case = changing_conditions_exhibit(\n", + " uspp.loc[\"Increasing Case\"],\n", + " 0.70,\n", + " [0, 0, 0, 0, 8509, 8934, 4690, 22162, 54296, 154745],\n", + ")\n", + "exhibit_iv_both = changing_conditions_exhibit(\n", + " uspp.loc[\"Increasing Claim Case\"],\n", + " 0.70,\n", + " [0, 0, 0, 0, 8509, 10210, 5695, 28494, 73688, 221064],\n", + ")\n", + "\n", + "print(\"Increasing Case Outstanding Strength\")\n", + "display(exhibit_iv_case)\n", + "display(\n", + " exhibit_iv_case[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")\n", + "\n", + "print(\"Increasing Claim Ratios and Case Outstanding Strength\")\n", + "display(exhibit_iv_both)\n", + "display(\n", + " exhibit_iv_both[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "acaa350f", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.474199Z", + "iopub.status.busy": "2026-07-23T16:32:58.473975Z", + "iopub.status.idle": "2026-07-23T16:32:58.477094Z", + "shell.execute_reply": "2026-07-23T16:32:58.476706Z" + } + }, + "outputs": [], + "source": [ + "# Exhibit IV Sheet 2 — reconcile to Friedland PDF p150\n", + "assert np.isclose(exhibit_iv_case[\"Estimated IBNR (6)\"].sum(), 253336, atol=1)\n", + "assert np.isclose(exhibit_iv_case[\"Difference (8)\"].sum(), 0, atol=1)\n", + "assert np.isclose(exhibit_iv_both[\"Estimated IBNR (6)\"].sum(), -1097165, atol=2)\n", + "assert np.isclose(exhibit_iv_both[\"Actual IBNR (7)\"].sum(), 347660, atol=1)\n", + "assert np.isclose(exhibit_iv_both[\"Difference (8)\"].sum(), 1444824, atol=3)" + ] + }, + { + "cell_type": "markdown", + "id": "2a558e27", + "metadata": {}, + "source": [ + "## P151 (Exhibit V)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "2636edf6", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.479423Z", + "iopub.status.busy": "2026-07-23T16:32:58.479238Z", + "iopub.status.idle": "2026-07-23T16:32:58.626414Z", + "shell.execute_reply": "2026-07-23T16:32:58.623915Z" + } + }, + "outputs": [ { - "cell_type": "markdown", - "id": "0e80110a", - "metadata": {}, - "source": [ - "## P150 (Exhibit IV Sheet 2)\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Steady-State (No Change in Product Mix)\n" + ] }, { - "cell_type": "code", - "execution_count": 22, - "id": "480249f8", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.352619Z", - "iopub.status.busy": "2026-07-23T16:32:58.352453Z", - "iopub.status.idle": "2026-07-23T16:32:58.471842Z", - "shell.execute_reply": "2026-07-23T16:32:58.471445Z" - } - }, - "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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08509.01.0
20041276282.00.7893397.0884463.08934.08934.00.0
20051340096.00.7938067.0933377.04690.04690.00.0
20061407100.00.7984970.0962808.022162.022162.00.0
20071477455.00.71034218.0979922.054296.054296.00.0
20081551328.00.71085930.0931185.0154745.0154745.00.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", - "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", - "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", - "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", - "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8509.0 1.0\n", - "2004 1276282.0 0.7 893397.0 884463.0 8934.0 8934.0 0.0\n", - "2005 1340096.0 0.7 938067.0 933377.0 4690.0 4690.0 0.0\n", - "2006 1407100.0 0.7 984970.0 962808.0 22162.0 22162.0 0.0\n", - "2007 1477455.0 0.7 1034218.0 979922.0 54296.0 54296.0 0.0\n", - "2008 1551328.0 0.7 1085930.0 931185.0 154745.0 154745.0 0.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.08551189.0253335.0253336.01.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 12577892.0 8804524.0 8551189.0 253335.0 253336.0 1.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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19991000000.00.7700000.0700000.00.00.00.0
20001050000.00.7735000.0735000.00.00.00.0
20011102500.00.7771750.0771750.00.00.00.0
20021157625.00.7810338.0810338.00.00.00.0
20031215506.00.7850854.0842346.08508.08509.01.0
20041276282.00.7893397.01010815.0-117418.010210.0127628.0
20051340096.00.7938067.01133386.0-195319.05695.0201014.0
20061407100.00.7984970.01237897.0-252927.028494.0281421.0
20071477455.00.71034218.01329895.0-295677.073688.0369365.0
20081551328.00.71085930.01330264.0-244334.0221064.0465398.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 1000000.0 0.7 700000.0 700000.0 0.0 0.0 0.0\n", - "2000 1050000.0 0.7 735000.0 735000.0 0.0 0.0 0.0\n", - "2001 1102500.0 0.7 771750.0 771750.0 0.0 0.0 0.0\n", - "2002 1157625.0 0.7 810338.0 810338.0 0.0 0.0 0.0\n", - "2003 1215506.0 0.7 850854.0 842346.0 8508.0 8509.0 1.0\n", - "2004 1276282.0 0.7 893397.0 1010815.0 -117418.0 10210.0 127628.0\n", - "2005 1340096.0 0.7 938067.0 1133386.0 -195319.0 5695.0 201014.0\n", - "2006 1407100.0 0.7 984970.0 1237897.0 -252927.0 28494.0 281421.0\n", - "2007 1477455.0 0.7 1034218.0 1329895.0 -295677.0 73688.0 369365.0\n", - "2008 1551328.0 0.7 1085930.0 1330264.0 -244334.0 221064.0 465398.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total12577892.08804524.09901691.0-1097167.0347660.01444827.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 12577892.0 8804524.0 9901691.0 -1097167.0 347660.0 1444827.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19992000000.00.751500000.01500000.00.00.00.0
20002100000.00.751575000.01575000.00.00.00.0
20012205000.00.751653750.01653750.00.00.00.0
20022315250.00.751736438.01736438.00.00.00.0
20032431013.00.751823260.01814751.08509.08509.00.0
20042552563.00.751914422.01885068.029354.029354.00.0
20052680191.00.752010143.01948499.061644.061644.00.0
20062814201.00.752110651.01937577.0173074.0173073.0-1.0
20072954911.00.752216183.01852729.0363454.0363454.00.0
20083102656.00.752326992.01568393.0758599.0758599.00.0
\n", + "
" ], - "source": [ - "exhibit_iv_case = changing_conditions_exhibit(\n", - " uspp.loc[\"Increasing Case\"],\n", - " 0.70,\n", - " [0, 0, 0, 0, 8509, 8934, 4690, 22162, 54296, 154745],\n", - ")\n", - "exhibit_iv_both = changing_conditions_exhibit(\n", - " uspp.loc[\"Increasing Claim Case\"],\n", - " 0.70,\n", - " [0, 0, 0, 0, 8509, 10210, 5695, 28494, 73688, 221064],\n", - ")\n", - "\n", - "print(\"Increasing Case Outstanding Strength\")\n", - "display(exhibit_iv_case)\n", - "display(exhibit_iv_case[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n", - "\n", - "print(\"Increasing Claim Ratios and Case Outstanding Strength\")\n", - "display(exhibit_iv_both)\n", - "display(exhibit_iv_both[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 2000000.0 0.75 1500000.0 1500000.0 0.0 0.0 0.0\n", + "2000 2100000.0 0.75 1575000.0 1575000.0 0.0 0.0 0.0\n", + "2001 2205000.0 0.75 1653750.0 1653750.0 0.0 0.0 0.0\n", + "2002 2315250.0 0.75 1736438.0 1736438.0 0.0 0.0 0.0\n", + "2003 2431013.0 0.75 1823260.0 1814751.0 8509.0 8509.0 0.0\n", + "2004 2552563.0 0.75 1914422.0 1885068.0 29354.0 29354.0 0.0\n", + "2005 2680191.0 0.75 2010143.0 1948499.0 61644.0 61644.0 0.0\n", + "2006 2814201.0 0.75 2110651.0 1937577.0 173074.0 173073.0 -1.0\n", + "2007 2954911.0 0.75 2216183.0 1852729.0 363454.0 363454.0 0.0\n", + "2008 3102656.0 0.75 2326992.0 1568393.0 758599.0 758599.0 0.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 23, - "id": "acaa350f", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.474199Z", - "iopub.status.busy": "2026-07-23T16:32:58.473975Z", - "iopub.status.idle": "2026-07-23T16:32:58.477094Z", - "shell.execute_reply": "2026-07-23T16:32:58.476706Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit IV Sheet 2 — reconcile to Friedland PDF p150\n", - "assert np.isclose(exhibit_iv_case[\"Estimated IBNR (6)\"].sum(), 253336, atol=1)\n", - "assert np.isclose(exhibit_iv_case[\"Difference (8)\"].sum(), 0, atol=1)\n", - "assert np.isclose(exhibit_iv_both[\"Estimated IBNR (6)\"].sum(), -1097165, atol=2)\n", - "assert np.isclose(exhibit_iv_both[\"Actual IBNR (7)\"].sum(), 347660, atol=1)\n", - "assert np.isclose(exhibit_iv_both[\"Difference (8)\"].sum(), 1444824, atol=3)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total25155785.018866839.017472205.01394634.01394633.0-1.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 25155785.0 18866839.0 17472205.0 1394634.0 1394633.0 -1.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "markdown", - "id": "2a558e27", - "metadata": {}, - "source": [ - "## P151 (Exhibit V)\n" - ] + "name": "stdout", + "output_type": "stream", + "text": [ + "Changing Product Mix\n" + ] }, { - "cell_type": "code", - "execution_count": 24, - "id": "2636edf6", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.479423Z", - "iopub.status.busy": "2026-07-23T16:32:58.479238Z", - "iopub.status.idle": "2026-07-23T16:32:58.626414Z", - "shell.execute_reply": "2026-07-23T16:32:58.623915Z" - } - }, - "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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19992000000.00.751500000.01500000.00.00.00.0
20002100000.00.751575000.01575000.00.00.00.0
20012205000.00.751653750.01653750.00.00.00.0
20022315250.00.751736438.01736438.00.00.00.0
20032431013.00.751823260.01814751.08509.08509.00.0
20042552563.00.751914422.01885068.029354.029354.00.0
20052680191.00.752010143.01948499.061644.061644.00.0
20062814201.00.752110651.01937577.0173074.0173073.0-1.0
20072954911.00.752216183.01852729.0363454.0363454.00.0
20083102656.00.752326992.01568393.0758599.0758599.00.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 2000000.0 0.75 1500000.0 1500000.0 0.0 0.0 0.0\n", - "2000 2100000.0 0.75 1575000.0 1575000.0 0.0 0.0 0.0\n", - "2001 2205000.0 0.75 1653750.0 1653750.0 0.0 0.0 0.0\n", - "2002 2315250.0 0.75 1736438.0 1736438.0 0.0 0.0 0.0\n", - "2003 2431013.0 0.75 1823260.0 1814751.0 8509.0 8509.0 0.0\n", - "2004 2552563.0 0.75 1914422.0 1885068.0 29354.0 29354.0 0.0\n", - "2005 2680191.0 0.75 2010143.0 1948499.0 61644.0 61644.0 0.0\n", - "2006 2814201.0 0.75 2110651.0 1937577.0 173074.0 173073.0 -1.0\n", - "2007 2954911.0 0.75 2216183.0 1852729.0 363454.0 363454.0 0.0\n", - "2008 3102656.0 0.75 2326992.0 1568393.0 758599.0 758599.0 0.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total25155785.018866839.017472205.01394634.01394633.0-1.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 25155785.0 18866839.0 17472205.0 1394634.0 1394633.0 -1.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", - "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19992000000.00.751500000.01500000.00.00.00.0
20002100000.00.751575000.01575000.00.00.00.0
20012205000.00.751653750.01653750.00.00.00.0
20022315250.00.751736438.01736438.00.00.00.0
20032431013.00.751823260.01814751.08509.08509.00.0
20042552563.00.751914422.01885068.029354.029354.00.0
20052999262.00.752249446.02193545.055902.071855.015953.0
20063564016.00.752673012.02471446.0201566.0239057.037491.0
20074281446.00.753211084.02680487.0530598.0596924.066326.0
20085196516.00.753897387.02556695.01340692.01445385.0104693.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "1999 2000000.0 0.75 1500000.0 1500000.0 0.0 0.0 0.0\n", - "2000 2100000.0 0.75 1575000.0 1575000.0 0.0 0.0 0.0\n", - "2001 2205000.0 0.75 1653750.0 1653750.0 0.0 0.0 0.0\n", - "2002 2315250.0 0.75 1736438.0 1736438.0 0.0 0.0 0.0\n", - "2003 2431013.0 0.75 1823260.0 1814751.0 8509.0 8509.0 0.0\n", - "2004 2552563.0 0.75 1914422.0 1885068.0 29354.0 29354.0 0.0\n", - "2005 2999262.0 0.75 2249446.0 2193545.0 55902.0 71855.0 15953.0\n", - "2006 3564016.0 0.75 2673012.0 2471446.0 201566.0 239057.0 37491.0\n", - "2007 4281446.0 0.75 3211084.0 2680487.0 530598.0 596924.0 66326.0\n", - "2008 5196516.0 0.75 3897387.0 2556695.0 1340692.0 1445385.0 104693.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", - "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total29645066.022233799.020067180.02166621.02391084.0224463.0
\n", - "
" - ], - "text/plain": [ - " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", - "Total 29645066.0 22233799.0 20067180.0 2166621.0 2391084.0 224463.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", + "
Earned Premium (2)Claim Ratio (3)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
19992000000.00.751500000.01500000.00.00.00.0
20002100000.00.751575000.01575000.00.00.00.0
20012205000.00.751653750.01653750.00.00.00.0
20022315250.00.751736438.01736438.00.00.00.0
20032431013.00.751823260.01814751.08509.08509.00.0
20042552563.00.751914422.01885068.029354.029354.00.0
20052999262.00.752249446.02193545.055902.071855.015953.0
20063564016.00.752673012.02471446.0201566.0239057.037491.0
20074281446.00.753211084.02680487.0530598.0596924.066326.0
20085196516.00.753897387.02556695.01340692.01445385.0104693.0
\n", + "
" ], - "source": [ - "us_auto = cl.load_sample(\"friedland_us_auto\")\n", - "\n", - "exhibit_v_steady = changing_conditions_exhibit(\n", - " us_auto.loc[\"Steady State\"],\n", - " 0.75,\n", - " [0, 0, 0, 0, 8509, 29354, 61644, 173073, 363454, 758599],\n", - ")\n", - "exhibit_v_mix = changing_conditions_exhibit(\n", - " us_auto.loc[\"Changing Product Mix\"],\n", - " 0.75,\n", - " [0, 0, 0, 0, 8509, 29354, 71855, 239057, 596924, 1445385],\n", - ")\n", - "\n", - "print(\"Steady-State (No Change in Product Mix)\")\n", - "display(exhibit_v_steady)\n", - "display(exhibit_v_steady[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n", - "\n", - "print(\"Changing Product Mix\")\n", - "display(exhibit_v_mix)\n", - "display(exhibit_v_mix[[\"Earned Premium (2)\", \"Expected Claims (4)\", \"Reported (5)\", \"Estimated IBNR (6)\", \"Actual IBNR (7)\", \"Difference (8)\"]].sum().rename(\"Total\").to_frame().T)\n" + "text/plain": [ + " Earned Premium (2) Claim Ratio (3) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "1999 2000000.0 0.75 1500000.0 1500000.0 0.0 0.0 0.0\n", + "2000 2100000.0 0.75 1575000.0 1575000.0 0.0 0.0 0.0\n", + "2001 2205000.0 0.75 1653750.0 1653750.0 0.0 0.0 0.0\n", + "2002 2315250.0 0.75 1736438.0 1736438.0 0.0 0.0 0.0\n", + "2003 2431013.0 0.75 1823260.0 1814751.0 8509.0 8509.0 0.0\n", + "2004 2552563.0 0.75 1914422.0 1885068.0 29354.0 29354.0 0.0\n", + "2005 2999262.0 0.75 2249446.0 2193545.0 55902.0 71855.0 15953.0\n", + "2006 3564016.0 0.75 2673012.0 2471446.0 201566.0 239057.0 37491.0\n", + "2007 4281446.0 0.75 3211084.0 2680487.0 530598.0 596924.0 66326.0\n", + "2008 5196516.0 0.75 3897387.0 2556695.0 1340692.0 1445385.0 104693.0" ] + }, + "metadata": {}, + "output_type": "display_data" }, { - "cell_type": "code", - "execution_count": 25, - "id": "73e84efc", - "metadata": { - "execution": { - "iopub.execute_input": "2026-07-23T16:32:58.628688Z", - "iopub.status.busy": "2026-07-23T16:32:58.628544Z", - "iopub.status.idle": "2026-07-23T16:32:58.634275Z", - "shell.execute_reply": "2026-07-23T16:32:58.633821Z" - } - }, - "outputs": [], - "source": [ - "# Exhibit V — reconcile to Friedland PDF p151\n", - "assert np.allclose(\n", - " exhibit_v_steady[\"Earned Premium (2)\"],\n", - " [2000000, 2100000, 2205000, 2315250, 2431013, 2552563, 2680191, 2814201, 2954911, 3102656],\n", - " atol=1,\n", - ")\n", - "assert np.isclose(exhibit_v_steady[\"Earned Premium (2)\"].sum(), 25155785, atol=1)\n", - "assert np.isclose(exhibit_v_steady[\"Expected Claims (4)\"].sum(), 18866839, atol=1)\n", - "assert np.isclose(exhibit_v_steady[\"Estimated IBNR (6)\"].sum(), 1394634, atol=1)\n", - "assert np.isclose(exhibit_v_steady[\"Difference (8)\"].sum(), 0, atol=1)\n", - "\n", - "assert np.isclose(exhibit_v_mix[\"Earned Premium (2)\"].sum(), 29645066, atol=1)\n", - "assert np.isclose(exhibit_v_mix[\"Expected Claims (4)\"].sum(), 22233799, atol=1)\n", - "assert np.isclose(exhibit_v_mix[\"Estimated IBNR (6)\"].sum(), 2166620, atol=1)\n", - "assert np.isclose(exhibit_v_mix[\"Actual IBNR (7)\"].sum(), 2391084, atol=1)\n", - "assert np.isclose(exhibit_v_mix[\"Difference (8)\"].sum(), 224465, atol=2)\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", + "
Earned Premium (2)Expected Claims (4)Reported (5)Estimated IBNR (6)Actual IBNR (7)Difference (8)
Total29645066.022233799.020067180.02166621.02391084.0224463.0
\n", + "
" + ], + "text/plain": [ + " Earned Premium (2) Expected Claims (4) Reported (5) Estimated IBNR (6) Actual IBNR (7) Difference (8)\n", + "Total 29645066.0 22233799.0 20067180.0 2166621.0 2391084.0 224463.0" ] + }, + "metadata": {}, + "output_type": "display_data" } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.10.7" + ], + "source": [ + "us_auto = cl.load_sample(\"friedland_us_auto\")\n", + "\n", + "exhibit_v_steady = changing_conditions_exhibit(\n", + " us_auto.loc[\"Steady State\"],\n", + " 0.75,\n", + " [0, 0, 0, 0, 8509, 29354, 61644, 173073, 363454, 758599],\n", + ")\n", + "exhibit_v_mix = changing_conditions_exhibit(\n", + " us_auto.loc[\"Changing Product Mix\"],\n", + " 0.75,\n", + " [0, 0, 0, 0, 8509, 29354, 71855, 239057, 596924, 1445385],\n", + ")\n", + "\n", + "print(\"Steady-State (No Change in Product Mix)\")\n", + "display(exhibit_v_steady)\n", + "display(\n", + " exhibit_v_steady[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")\n", + "\n", + "print(\"Changing Product Mix\")\n", + "display(exhibit_v_mix)\n", + "display(\n", + " exhibit_v_mix[\n", + " [\n", + " \"Earned Premium (2)\",\n", + " \"Expected Claims (4)\",\n", + " \"Reported (5)\",\n", + " \"Estimated IBNR (6)\",\n", + " \"Actual IBNR (7)\",\n", + " \"Difference (8)\",\n", + " ]\n", + " ]\n", + " .sum()\n", + " .rename(\"Total\")\n", + " .to_frame()\n", + " .T\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "73e84efc", + "metadata": { + "execution": { + "iopub.execute_input": "2026-07-23T16:32:58.628688Z", + "iopub.status.busy": "2026-07-23T16:32:58.628544Z", + "iopub.status.idle": "2026-07-23T16:32:58.634275Z", + "shell.execute_reply": "2026-07-23T16:32:58.633821Z" } + }, + "outputs": [], + "source": [ + "# Exhibit V — reconcile to Friedland PDF p151\n", + "assert np.allclose(\n", + " exhibit_v_steady[\"Earned Premium (2)\"],\n", + " [\n", + " 2000000,\n", + " 2100000,\n", + " 2205000,\n", + " 2315250,\n", + " 2431013,\n", + " 2552563,\n", + " 2680191,\n", + " 2814201,\n", + " 2954911,\n", + " 3102656,\n", + " ],\n", + " atol=1,\n", + ")\n", + "assert np.isclose(exhibit_v_steady[\"Earned Premium (2)\"].sum(), 25155785, atol=1)\n", + "assert np.isclose(exhibit_v_steady[\"Expected Claims (4)\"].sum(), 18866839, atol=1)\n", + "assert np.isclose(exhibit_v_steady[\"Estimated IBNR (6)\"].sum(), 1394634, atol=1)\n", + "assert np.isclose(exhibit_v_steady[\"Difference (8)\"].sum(), 0, atol=1)\n", + "\n", + "assert np.isclose(exhibit_v_mix[\"Earned Premium (2)\"].sum(), 29645066, atol=1)\n", + "assert np.isclose(exhibit_v_mix[\"Expected Claims (4)\"].sum(), 22233799, atol=1)\n", + "assert np.isclose(exhibit_v_mix[\"Estimated IBNR (6)\"].sum(), 2166620, atol=1)\n", + "assert np.isclose(exhibit_v_mix[\"Actual IBNR (7)\"].sum(), 2391084, atol=1)\n", + "assert np.isclose(exhibit_v_mix[\"Difference (8)\"].sum(), 224465, atol=2)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" }, - "nbformat": 4, - "nbformat_minor": 5 + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.7" + } + }, + "nbformat": 4, + "nbformat_minor": 5 } diff --git a/docs/gallery/plot_berqsherm_case.ipynb b/docs/gallery/plot_berqsherm_case.ipynb index 9dce9b497..c3797d364 100644 --- a/docs/gallery/plot_berqsherm_case.ipynb +++ b/docs/gallery/plot_berqsherm_case.ipynb @@ -47,18 +47,21 @@ "outputs": [], "source": [ "# Load data\n", - "triangle = cl.load_sample('berqsherm').loc['MedMal']\n", + "triangle = cl.load_sample(\"berqsherm\").loc[\"MedMal\"]\n", "\n", "# Specify Berquist-Sherman model\n", "berq = cl.BerquistSherman(\n", - " paid_amount='Paid', incurred_amount='Incurred',\n", - " reported_count='Reported', closed_count='Closed',\n", - " trend=0.15)\n", + " paid_amount=\"Paid\",\n", + " incurred_amount=\"Incurred\",\n", + " reported_count=\"Reported\",\n", + " closed_count=\"Closed\",\n", + " trend=0.15,\n", + ")\n", "\n", "# Adjust our triangle data\n", "berq_triangle = berq.fit_transform(triangle)\n", - "berq_cdf = cl.Development().fit(berq_triangle['Incurred']).cdf_\n", - "orig_cdf = cl.Development().fit(triangle['Incurred']).cdf_" + "berq_cdf = cl.Development().fit(berq_triangle[\"Incurred\"]).cdf_\n", + "orig_cdf = cl.Development().fit(triangle[\"Incurred\"]).cdf_" ] }, { @@ -88,15 +91,19 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "# Plot the results\n", "ax = (berq_cdf / orig_cdf).T.plot(\n", - " kind='bar', grid=True, legend=False,\n", - " title='Berquist Sherman CDF to Unadjusted CDF',\n", - " xlabel='Age to Ultimate', \n", - " ylabel='Case Incurred CDF Adjustment');" + " kind=\"bar\",\n", + " grid=True,\n", + " legend=False,\n", + " title=\"Berquist Sherman CDF to Unadjusted CDF\",\n", + " xlabel=\"Age to Ultimate\",\n", + " ylabel=\"Case Incurred CDF Adjustment\",\n", + ");" ] }, { diff --git a/docs/gallery/plot_bf_apriori_from_cl.ipynb b/docs/gallery/plot_bf_apriori_from_cl.ipynb index 5b36109b3..54eb2e7ac 100644 --- a/docs/gallery/plot_bf_apriori_from_cl.ipynb +++ b/docs/gallery/plot_bf_apriori_from_cl.ipynb @@ -21,7 +21,7 @@ "outputs": [], "source": [ "import chainladder as cl\n", - "import pandas as pd\n" + "import pandas as pd" ] }, { @@ -47,16 +47,19 @@ "outputs": [], "source": [ "# Create Aprioris as the mean AY chainladder ultimate\n", - "raa = cl.load_sample('RAA')\n", + "raa = cl.load_sample(\"RAA\")\n", "\n", "cl_ult = cl.Chainladder().fit(raa).ultimate_ # Chainladder Ultimate\n", "apriori = cl_ult * 0 + (cl_ult.sum() / 10) # Mean Chainladder Ultimate\n", "bf_ult = cl.BornhuetterFerguson(apriori=1).fit(raa, sample_weight=apriori).ultimate_\n", "\n", "output = pd.concat(\n", - " (cl_ult.to_frame().rename({'2261': 'Chainladder'}, axis=1),\n", - " bf_ult.to_frame().rename({'2261': 'BornhuetterFerguson'}, axis=1)),\n", - " axis=1)" + " (\n", + " cl_ult.to_frame().rename({\"2261\": \"Chainladder\"}, axis=1),\n", + " bf_ult.to_frame().rename({\"2261\": \"BornhuetterFerguson\"}, axis=1),\n", + " ),\n", + " axis=1,\n", + ")" ] }, { @@ -86,13 +89,13 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "# Plot of Ultimates\n", "\n", - "ax = output.plot(grid=True, marker='o', \n", - " xlabel='Accident Year', ylabel='Ultimate');" + "ax = output.plot(grid=True, marker=\"o\", xlabel=\"Accident Year\", ylabel=\"Ultimate\");" ] }, { diff --git a/docs/gallery/plot_callable_dev_constant.ipynb b/docs/gallery/plot_callable_dev_constant.ipynb index f52b49981..82cb0c00b 100644 --- a/docs/gallery/plot_callable_dev_constant.ipynb +++ b/docs/gallery/plot_callable_dev_constant.ipynb @@ -67,24 +67,27 @@ ], "source": [ "# Sample Data\n", - "agway = cl.load_sample('clrd').loc['Agway Ins Co', 'CumPaidLoss']\n", + "agway = cl.load_sample(\"clrd\").loc[\"Agway Ins Co\", \"CumPaidLoss\"]\n", + "\n", "\n", "def paid_cdfs(x):\n", - " \"\"\" A function that returns different CDFs depending on a specified LOB \"\"\"\n", + " \"\"\"A function that returns different CDFs depending on a specified LOB\"\"\"\n", " cdfs = {\n", - " 'comauto': [3.832, 1.874, 1.386, 1.181, 1.085, 1.043, 1.022, 1.013, 1.007, 1],\n", - " 'medmal': [24.168, 4.127, 2.103, 1.528, 1.275, 1.161, 1.088, 1.047, 1.018, 1],\n", - " 'othliab': [10.887, 3.416, 1.957, 1.433, 1.231, 1.119, 1.06, 1.031, 1.011, 1],\n", - " 'ppauto': [2.559, 1.417, 1.181, 1.084, 1.04, 1.019, 1.009, 1.004, 1.001, 1],\n", - " 'prodliab': [13.703, 5.613, 2.92, 1.765, 1.385, 1.177, 1.072, 1.034, 1.008, 1],\n", - " 'wkcomp': [4.106, 1.865, 1.418, 1.234, 1.141, 1.09, 1.056, 1.03, 1.01, 1]}\n", + " \"comauto\": [3.832, 1.874, 1.386, 1.181, 1.085, 1.043, 1.022, 1.013, 1.007, 1],\n", + " \"medmal\": [24.168, 4.127, 2.103, 1.528, 1.275, 1.161, 1.088, 1.047, 1.018, 1],\n", + " \"othliab\": [10.887, 3.416, 1.957, 1.433, 1.231, 1.119, 1.06, 1.031, 1.011, 1],\n", + " \"ppauto\": [2.559, 1.417, 1.181, 1.084, 1.04, 1.019, 1.009, 1.004, 1.001, 1],\n", + " \"prodliab\": [13.703, 5.613, 2.92, 1.765, 1.385, 1.177, 1.072, 1.034, 1.008, 1],\n", + " \"wkcomp\": [4.106, 1.865, 1.418, 1.234, 1.141, 1.09, 1.056, 1.03, 1.01, 1],\n", + " }\n", " patterns = pd.DataFrame(cdfs, index=range(12, 132, 12)).T\n", - " return patterns.loc[x.loc['LOB']].to_dict()\n", + " return patterns.loc[x.loc[\"LOB\"]].to_dict()\n", + "\n", "\n", "# If it works with pandas apply on the triangle index...\n", "agway.index.apply(paid_cdfs, axis=1)\n", "# ... then it will work in DevelopmentConstant\n", - "model = cl.DevelopmentConstant(patterns=paid_cdfs, callable_axis=0, style='cdf')\n", + "model = cl.DevelopmentConstant(patterns=paid_cdfs, callable_axis=0, style=\"cdf\")\n", "\n", "model.fit(agway)" ] @@ -116,10 +119,11 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", - "ax = model.ldf_.T.plot(kind='bar', title='Agway Insurance LDFs');" + "ax = model.ldf_.T.plot(kind=\"bar\", title=\"Agway Insurance LDFs\");" ] }, { diff --git a/docs/gallery/plot_clarkldf.ipynb b/docs/gallery/plot_clarkldf.ipynb index 5484a7add..7b7356b80 100644 --- a/docs/gallery/plot_clarkldf.ipynb +++ b/docs/gallery/plot_clarkldf.ipynb @@ -20,7 +20,7 @@ "outputs": [], "source": [ "import chainladder as cl\n", - "import numpy as np\n" + "import numpy as np" ] }, { @@ -45,12 +45,12 @@ "outputs": [], "source": [ "# Grab Industry triangles\n", - "clrd = cl.load_sample('clrd').groupby('LOB').sum()\n", + "clrd = cl.load_sample(\"clrd\").groupby(\"LOB\").sum()\n", "\n", "# Fit Clark Cape Cod method\n", - "model = cl.ClarkLDF(growth='loglogistic').fit(\n", - " clrd['CumPaidLoss'],\n", - " sample_weight=clrd['EarnedPremDIR'].latest_diagonal)\n", + "model = cl.ClarkLDF(growth=\"loglogistic\").fit(\n", + " clrd[\"CumPaidLoss\"], sample_weight=clrd[\"EarnedPremDIR\"].latest_diagonal\n", + ")\n", "\n", "# sample ages\n", "ages = np.linspace(1, 300, 30)\n", @@ -86,12 +86,13 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "ax = results.plot(\n", - " title='Loglogistic Growth Curves',\n", - " xlabel='Age', ylabel='% of Ultimate');" + " title=\"Loglogistic Growth Curves\", xlabel=\"Age\", ylabel=\"% of Ultimate\"\n", + ");" ] } ], diff --git a/docs/gallery/plot_exponential_smoothing.ipynb b/docs/gallery/plot_exponential_smoothing.ipynb index 013af358a..7cfefdd9b 100644 --- a/docs/gallery/plot_exponential_smoothing.ipynb +++ b/docs/gallery/plot_exponential_smoothing.ipynb @@ -46,12 +46,15 @@ }, "outputs": [], "source": [ - "raa = cl.load_sample('raa')\n", + "raa = cl.load_sample(\"raa\")\n", "\n", - "results = pd.concat((\n", - " cl.TailCurve().fit(raa).ldf_.T.iloc[:, 0].rename('Unsmoothed'),\n", - " cl.TailCurve(attachment_age=12).fit(raa).ldf_.T.iloc[:, 0].rename('Curve Fit')\n", - "), axis=1)" + "results = pd.concat(\n", + " (\n", + " cl.TailCurve().fit(raa).ldf_.T.iloc[:, 0].rename(\"Unsmoothed\"),\n", + " cl.TailCurve(attachment_age=12).fit(raa).ldf_.T.iloc[:, 0].rename(\"Curve Fit\"),\n", + " ),\n", + " axis=1,\n", + ")" ] }, { @@ -77,10 +80,11 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", - "ax = results.plot(title='Exponential Smoothing of LDF');" + "ax = results.plot(title=\"Exponential Smoothing of LDF\");" ] }, { diff --git a/docs/gallery/plot_glm_ldf.ipynb b/docs/gallery/plot_glm_ldf.ipynb index c3120718b..13ec3da46 100644 --- a/docs/gallery/plot_glm_ldf.ipynb +++ b/docs/gallery/plot_glm_ldf.ipynb @@ -45,16 +45,16 @@ }, "outputs": [], "source": [ - "genins = cl.load_sample('genins')\n", + "genins = cl.load_sample(\"genins\")\n", "\n", "# Fit an ODP GLM\n", "dev = cl.TweedieGLM(\n", - " design_matrix='C(development) + C(origin)',\n", - " link='log', power=1).fit(genins)\n", + " design_matrix=\"C(development) + C(origin)\", link=\"log\", power=1\n", + ").fit(genins)\n", "\n", "# Grab LDFs vs traditional approach\n", - "glm = dev.ldf_.iloc[..., 0, :].T.iloc[:, 0].rename('GLM')\n", - "traditional = cl.Development().fit(genins).ldf_.T.iloc[:, 0].rename('Traditional')\n", + "glm = dev.ldf_.iloc[..., 0, :].T.iloc[:, 0].rename(\"GLM\")\n", + "traditional = cl.Development().fit(genins).ldf_.T.iloc[:, 0].rename(\"Traditional\")\n", "\n", "# Plot data\n", "results = pd.concat((glm, traditional), axis=1)" @@ -87,10 +87,11 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", - "ax = results.plot(kind='bar', title='LDF: Poisson GLM vs Traditional');" + "ax = results.plot(kind=\"bar\", title=\"LDF: Poisson GLM vs Traditional\");" ] } ], diff --git a/docs/gallery/plot_ibnr_runoff.ipynb b/docs/gallery/plot_ibnr_runoff.ipynb index 5ead12efb..6a2b04b74 100644 --- a/docs/gallery/plot_ibnr_runoff.ipynb +++ b/docs/gallery/plot_ibnr_runoff.ipynb @@ -45,17 +45,17 @@ "outputs": [], "source": [ "# Create a triangle\n", - "triangle = cl.load_sample('genins')\n", + "triangle = cl.load_sample(\"genins\")\n", "\n", "# Fit a model\n", "model = cl.Chainladder().fit(triangle)\n", "\n", "# Develop IBNR runoff triangle\n", - "runoff = (model.full_triangle_.cum_to_incr() - triangle.cum_to_incr())\n", + "runoff = model.full_triangle_.cum_to_incr() - triangle.cum_to_incr()\n", "\n", "# Convert to calendar period and aggregate across all accident years\n", "cal_yr_runoff = runoff[runoff.valuation > triangle.valuation_date]\n", - "cal_yr_runoff = cal_yr_runoff.dev_to_val().sum(axis='origin')" + "cal_yr_runoff = cal_yr_runoff.dev_to_val().sum(axis=\"origin\")" ] }, { @@ -85,14 +85,19 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "# Plot results\n", "ax = cal_yr_runoff.dropna().T.plot(\n", - " kind='bar', legend=False,\n", - " title='GenIns: IBNR Run-off', alpha=0.7,\n", - " xlabel='Calendar Year', ylabel='IBNR');" + " kind=\"bar\",\n", + " legend=False,\n", + " title=\"GenIns: IBNR Run-off\",\n", + " alpha=0.7,\n", + " xlabel=\"Calendar Year\",\n", + " ylabel=\"IBNR\",\n", + ");" ] } ], diff --git a/docs/gallery/plot_industry_to_company.ipynb b/docs/gallery/plot_industry_to_company.ipynb index 3326cc625..0df61cec0 100644 --- a/docs/gallery/plot_industry_to_company.ipynb +++ b/docs/gallery/plot_industry_to_company.ipynb @@ -155,22 +155,26 @@ } ], "source": [ - "clrd = cl.load_sample('clrd')['CumPaidLoss']\n", - "clrd_wc = clrd[clrd['LOB'] == 'wkcomp']\n", + "clrd = cl.load_sample(\"clrd\")[\"CumPaidLoss\"]\n", + "clrd_wc = clrd[clrd[\"LOB\"] == \"wkcomp\"]\n", "\n", "industry_wc = clrd_wc.sum()\n", "\n", - "allstate_wc_industry_pattern = cl.Chainladder().fit(industry_wc).predict(clrd_wc.loc['Allstate Ins Co Grp']).ultimate_\n", - "allstate_wc_company_pattern = cl.Chainladder().fit(clrd_wc.loc['Allstate Ins Co Grp']).ultimate_\n", + "allstate_wc_industry_pattern = (\n", + " cl\n", + " .Chainladder()\n", + " .fit(industry_wc)\n", + " .predict(clrd_wc.loc[\"Allstate Ins Co Grp\"])\n", + " .ultimate_\n", + ")\n", + "allstate_wc_company_pattern = (\n", + " cl.Chainladder().fit(clrd_wc.loc[\"Allstate Ins Co Grp\"]).ultimate_\n", + ")\n", "\n", - "df = pd.DataFrame(\n", - " {\n", - " \"allstate_wc_industry_pattern\": allstate_wc_industry_pattern.to_frame(\n", - " ).iloc[:, 0],\n", - " \"allstate_wc_company_pattern\": allstate_wc_company_pattern.to_frame(\n", - " ).iloc[:, 0],\n", - " }\n", - ").fillna(0)\n", + "df = pd.DataFrame({\n", + " \"allstate_wc_industry_pattern\": allstate_wc_industry_pattern.to_frame().iloc[:, 0],\n", + " \"allstate_wc_company_pattern\": allstate_wc_company_pattern.to_frame().iloc[:, 0],\n", + "}).fillna(0)\n", "df.index = pd.to_datetime(df.index).year\n", "df.index.name = \"Origin Year\"\n", "df.reset_index()" @@ -204,16 +208,15 @@ "source": [ "import matplotlib.pyplot as plt\n", "\n", - "plot_df = df[[\n", - " \"allstate_wc_industry_pattern\",\n", - " \"allstate_wc_company_pattern\"\n", - "]].rename(columns={\n", - " \"allstate_wc_industry_pattern\": \"Industry Pattern\",\n", - " \"allstate_wc_company_pattern\": \"Company Pattern\"\n", - "})\n", + "plot_df = df[[\"allstate_wc_industry_pattern\", \"allstate_wc_company_pattern\"]].rename(\n", + " columns={\n", + " \"allstate_wc_industry_pattern\": \"Industry Pattern\",\n", + " \"allstate_wc_company_pattern\": \"Company Pattern\",\n", + " }\n", + ")\n", "\n", "# Create bar chart\n", - "plot_df.plot(kind='bar')\n", + "plot_df.plot(kind=\"bar\")\n", "\n", "# Labels and title\n", "plt.title(\"Allstate WC Ultimate Comparison\")\n", diff --git a/docs/gallery/plot_tailcurve_compare.ipynb b/docs/gallery/plot_tailcurve_compare.ipynb index 9026e8ab5..e2d2f531b 100644 --- a/docs/gallery/plot_tailcurve_compare.ipynb +++ b/docs/gallery/plot_tailcurve_compare.ipynb @@ -43,12 +43,13 @@ }, "outputs": [], "source": [ - "clrd = cl.load_sample('clrd').groupby('LOB').sum()['CumPaidLoss']\n", - "cdf_ip = cl.TailCurve(curve='inverse_power').fit(clrd)\n", - "cdf_xp = cl.TailCurve(curve='exponential').fit(clrd)\n", + "clrd = cl.load_sample(\"clrd\").groupby(\"LOB\").sum()[\"CumPaidLoss\"]\n", + "cdf_ip = cl.TailCurve(curve=\"inverse_power\").fit(clrd)\n", + "cdf_xp = cl.TailCurve(curve=\"exponential\").fit(clrd)\n", "\n", - "result = pd.concat((cdf_ip.tail_.rename(\"Inverse Power\"),\n", - " cdf_xp.tail_.rename(\"Exponential\")), axis=1)\n" + "result = pd.concat(\n", + " (cdf_ip.tail_.rename(\"Inverse Power\"), cdf_xp.tail_.rename(\"Exponential\")), axis=1\n", + ")" ] }, { @@ -78,12 +79,13 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "ax = result.plot(\n", - " kind='bar', title='Curve Fit Comparison',\n", - " xlabel='Industry', ylabel='Tail Factor');" + " kind=\"bar\", title=\"Curve Fit Comparison\", xlabel=\"Industry\", ylabel=\"Tail Factor\"\n", + ");" ] }, { diff --git a/docs/gallery/plot_triangle_slicing.ipynb b/docs/gallery/plot_triangle_slicing.ipynb index 819c368fa..911d0dda3 100644 --- a/docs/gallery/plot_triangle_slicing.ipynb +++ b/docs/gallery/plot_triangle_slicing.ipynb @@ -90,10 +90,10 @@ ], "source": [ "# Load data\n", - "clrd = cl.load_sample('clrd')\n", + "clrd = cl.load_sample(\"clrd\")\n", "\n", "# pandas-style Aggregations\n", - "clrd = clrd.groupby('LOB').sum()\n", + "clrd = clrd.groupby(\"LOB\").sum()\n", "clrd" ] }, @@ -276,9 +276,9 @@ ], "source": [ "# pandas-style value/column slicing\n", - "clrd = clrd['CumPaidLoss']\n", + "clrd = clrd[\"CumPaidLoss\"]\n", "# pandas loc-style index slicing\n", - "clrd = clrd.loc['medmal']\n", + "clrd = clrd.loc[\"medmal\"]\n", "clrd" ] }, @@ -305,14 +305,17 @@ ], "source": [ "import matplotlib.pyplot as plt\n", - "plt.style.use('ggplot')\n", + "\n", + "plt.style.use(\"ggplot\")\n", "%config InlineBackend.figure_format = 'retina'\n", "\n", "# Plot\n", "ax = clrd.link_ratio.T.plot(\n", - " marker='o', \n", - " title='Medical Malpractice Link Ratios',\n", - " ylabel='Link Ratio', xlabel='Accident Year');" + " marker=\"o\",\n", + " title=\"Medical Malpractice Link Ratios\",\n", + " ylabel=\"Link Ratio\",\n", + " xlabel=\"Accident Year\",\n", + ");" ] } ], diff --git a/docs/getting_started/online_sandbox/sandbox_workbook_filled.ipynb b/docs/getting_started/online_sandbox/sandbox_workbook_filled.ipynb index 5f8b31f98..01c2e0b6c 100644 --- a/docs/getting_started/online_sandbox/sandbox_workbook_filled.ipynb +++ b/docs/getting_started/online_sandbox/sandbox_workbook_filled.ipynb @@ -3978,15 +3978,13 @@ ], "source": [ "cc_result = (\n", - " cl.CapeCod()\n", + " cl\n", + " .CapeCod()\n", " .fit(xyz_tri[\"Incurred\"], sample_weight=xyz_tri[\"Premium\"].latest_diagonal)\n", " .ultimate_\n", ").to_frame()\n", "\n", - "plt.plot(\n", - " cc_result.index.year, \n", - " cc_result[\"2261\"]\n", - ")" + "plt.plot(cc_result.index.year, cc_result[\"2261\"])" ] }, { diff --git a/docs/prep_sphinx_conf.py b/docs/prep_sphinx_conf.py index ca7cfb7e6..d54846826 100644 --- a/docs/prep_sphinx_conf.py +++ b/docs/prep_sphinx_conf.py @@ -4,6 +4,7 @@ `jupyter-book config sphinx` lists local extensions but does not emit this path, so standalone Sphinx (e.g. Read the Docs) cannot import them. Run after generating conf.py. """ + from pathlib import Path CONF = Path(__file__).resolve().parent / "conf.py" diff --git a/docs/user_guide/utilities.ipynb b/docs/user_guide/utilities.ipynb index deb85ee64..0ceba8405 100644 --- a/docs/user_guide/utilities.ipynb +++ b/docs/user_guide/utilities.ipynb @@ -2,6 +2,7 @@ "cells": [ { "cell_type": "markdown", + "id": "7fb27b941602401d91542211134fc71a", "metadata": {}, "source": [ "# Utilities\n", @@ -21,6 +22,7 @@ { "cell_type": "code", "execution_count": null, + "id": "acae54e37e7d407bbb7b55eff062a284", "metadata": {}, "outputs": [], "source": [ @@ -31,6 +33,7 @@ }, { "cell_type": "markdown", + "id": "9a63283cbaf04dbcab1f6479b197f3a8", "metadata": {}, "source": [ "## Model Diagnostics\n", @@ -44,15 +47,17 @@ { "cell_type": "code", "execution_count": null, + "id": "8dd0d8092fe74a7c96281538738b07e2", "metadata": {}, "outputs": [], "source": [ - "model = cl.Chainladder().fit(cl.load_sample('raa'))\n", + "model = cl.Chainladder().fit(cl.load_sample(\"raa\"))\n", "cl.model_diagnostics(model)" ] }, { "cell_type": "markdown", + "id": "72eea5119410473aa328ad9291626812", "metadata": {}, "source": [ "## Chainladder Persistence\n", @@ -81,7 +86,8 @@ ], "source": [ "import chainladder as cl\n", - "model_json = cl.Chainladder().fit(cl.load_sample('raa')).to_json()\n", + "\n", + "model_json = cl.Chainladder().fit(cl.load_sample(\"raa\")).to_json()\n", "model_json" ] }, @@ -136,10 +142,10 @@ ], "source": [ "# Dumping triangle to JSON\n", - "triangle_json = cl.load_sample('raa').to_json()\n", + "triangle_json = cl.load_sample(\"raa\").to_json()\n", "\n", "# Recalling model and Triangle and rehydrating the results\n", - "cl.read_json(model_json).fit(cl.read_json(triangle_json)).ibnr_.sum('origin')" + "cl.read_json(model_json).fit(cl.read_json(triangle_json)).ibnr_.sum(\"origin\")" ] }, { From 08d0120b62189a137956c03dd505a5707e68cd84 Mon Sep 17 00:00:00 2001 From: Gene Dan Date: Tue, 15 Sep 2026 15:07:24 -0500 Subject: [PATCH 2/2] [CHORE] Remove patch from Ruff workflow. --- .github/workflows/ruff.yml | 41 +++----------------------------------- .pre-commit-config.yaml | 10 ++-------- pyproject.toml | 9 ++++----- 3 files changed, 9 insertions(+), 51 deletions(-) diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml index 13db90949..c2fcbcc24 100644 --- a/.github/workflows/ruff.yml +++ b/.github/workflows/ruff.yml @@ -20,52 +20,17 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Determine changed Python/notebook files - id: changed - run: | - if [ "${{ github.event_name }}" = "pull_request" ]; then - base="origin/${{ github.event.pull_request.base.ref }}" - elif [ "${{ github.ref }}" = "refs/heads/main" ]; then - # origin/main already reflects this push after checkout, so diffing - # against it here would always be empty. Use the pre-push state instead. - base="${{ github.event.before }}" - # New branch or force-push: 'before' may not exist locally (or be all-zeros). - if ! git cat-file -e "$base" 2>/dev/null; then - base="HEAD^" - git cat-file -e "$base" 2>/dev/null || base="" - fi - else - base="origin/main" - fi - - # Base ref not fetched/available for some reason: fall back to checking everything. - if [ -n "$base" ] && ! git cat-file -e "$base" 2>/dev/null; then - base="" - fi - - if [ -z "$base" ]; then - files=$(git ls-files '*.py' '*.ipynb' | xargs) - else - files=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD -- '*.py' '*.ipynb' | xargs) - fi - - echo "files=$files" >> "$GITHUB_OUTPUT" - name: Install uv - if: steps.changed.outputs.files != '' uses: astral-sh/setup-uv@v7 with: version: "latest" - name: Run ruff - if: steps.changed.outputs.files != '' run: | - uvx ruff@0.16.1 check --force-exclude --config lint.per-file-ignores={} --output-format=github ${{ steps.changed.outputs.files }} + uvx ruff@0.16.1 check --force-exclude --output-format=github . - name: Run ruff format - if: (success() || failure()) && steps.changed.outputs.files != '' + if: success() || failure() run: | - uvx ruff@0.16.1 format --check --diff --force-exclude ${{ steps.changed.outputs.files }} + uvx ruff@0.16.1 format --check --diff --force-exclude . diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ddcdbf7be..740f435ed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -6,14 +6,8 @@ repos: - id: ruff name: ruff entry: bash -c ' - base=$(git rev-parse --verify --quiet origin/main || git rev-parse --verify --quiet main) && - files=$(git diff --name-only --diff-filter=ACMR "$base"...HEAD -- "*.py" "*.ipynb") && - if [ -z "$files" ]; then - echo "ruff - no changed files to check"; - else - uv run ruff check --force-exclude --config lint.per-file-ignores={} $files && - uv run ruff format --check --diff --force-exclude $files; - fi' + uv run ruff check --force-exclude . && + uv run ruff format --check --diff --force-exclude .' language: system pass_filenames: false always_run: true diff --git a/pyproject.toml b/pyproject.toml index b5edad140..f60e19ee8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -117,11 +117,10 @@ select = [ "UP034", ] -# Grandfathered violations that predate enabling this rule set. New and -# newly-touched code is still held to the full ruleset above; these files -# are exempted only from the specific codes they already violated so the -# check can be required without blocking unrelated PRs. Remove entries as -# files are cleaned up. +# Grandfathered violations that predate enabling this rule set. Everything +# else in the repo is held to the full ruleset above; these files are +# exempted only from the specific codes they already violated. Remove +# entries as files are cleaned up. [tool.ruff.lint.per-file-ignores] "docs/friedland/chapter_10.ipynb" = ["E731", "F841"] "docs/friedland/chapter_7_part_2.ipynb" = ["N802"]