kernels.MackFit
A fitted distribution-free chain ladder on one cohort.
Usage
kernels.MackFit(
cum,
obs_mask,
latest_dev,
f,
sigma2,
s,
n_obs,
n_pos,
origin_periods,
dev_grain_months,
sigma_rule,
units=None,
loss_field=None
)Arrays are 0-based on both axes: dev index j spans 0 .. n_d - 1 and the development step j -> j + 1 carries f[j], sigma2[j], s[j] (its volume denominator), n_obs[j] (the origins behind the FACTOR) and n_pos[j] (the origins behind the SIGMA), each of length n_d - 1. The last two differ only where a pair origin has a zero cumulative; see the module docstring.
cum keeps the observed triangle (NaN outside it); full is the same matrix with the lower triangle filled by the chain-ladder projection, so full[:, -1] is the ultimate and full[i, j] for j > latest_dev[i] is the C-hat_{i,j} that Mack’s and Merz-Wuthrich’s variance formulas both evaluate at.
Parameter Attributes
cum: np.ndarrayobs_mask: np.ndarraylatest_dev: np.ndarrayf: np.ndarraysigma2: np.ndarrays: np.ndarrayn_obs: np.ndarrayn_pos: np.ndarrayorigin_periods: list[dt.date]dev_grain_months: intsigma_rule: strunits: str | None = Noneloss_field: str | None = None
Attributes
| Name | Description |
|---|---|
| full | (n_w, n_d) observed triangle completed by the chain-ladder projection. |
| latest | (n_w,) each origin’s cumulative loss on the latest diagonal. |
| reserve | (n_w,) IBNR = ultimate - latest. Zero for a fully developed origin. |
| ultimate | (n_w,) projected ultimate = the completed triangle’s last column. |
full
(n_w, n_d) observed triangle completed by the chain-ladder projection.
full: np.ndarray
latest
(n_w,) each origin’s cumulative loss on the latest diagonal.
latest: np.ndarray
reserve
(n_w,) IBNR = ultimate - latest. Zero for a fully developed origin.
reserve: np.ndarray
ultimate
(n_w,) projected ultimate = the completed triangle’s last column.
ultimate: np.ndarray
Methods
| Name | Description |
|---|---|
| from_arrow() | Decode a fit written by to_arrow(), refusing any other kind - |
| msep_runoff() | Mack’s conditional MSEP of the FULL run-off reserve. |
| require_positive_open_diagonals() | Every OPEN origin’s latest-diagonal cell must be strictly positive. |
| summary() | One row per origin: latest, ultimate, IBNR and the run-off standard |
| to_arrow() |
Arrow IPC bytes. Carries n_obs and n_pos separately, because
|
from_arrow()
Decode a fit written by to_arrow(), refusing any other kind -
Usage
from_arrow(data)including the MackFitPanel it may well have come out of.
msep_runoff()
Mack’s conditional MSEP of the FULL run-off reserve.
Usage
msep_runoff()Mack (1993) formula (3), per accident year i:
msep_i = C-hat_{i,J}^2 * sum_{j=k_i}^{J-1} (sigma_j^2 / f_j^2)
* (1 / C-hat_{i,j} + 1 / S_j)
where k_i = latest_dev[i] is the dev index of i’s diagonal cell and J = n_d - 1. The 1/C-hat term is process risk, the 1/S_j term estimation risk; both are returned separately because cl.MackChainladder exposes them separately and the tie-out checks each. For the aggregate, Mack’s second formula adds the estimation-risk covariance between accident years, which share the same estimated factors:
msep_total = sum_i msep_i
+ 2 * sum_{i<k} C-hat_{i,J} C-hat_{k,J}
* sum_{j=k_i}^{J-1} (sigma_j^2 / f_j^2) / S_j
(the inner sum runs over the OLDER year’s dev range, which is the intersection of the two ranges). Process risk carries no cross term: accident years are independent under Mack’s assumptions.
Returns msep / process / parameter per origin (variances, not standard errors) plus the scalars msep_total, process_total, parameter_total.
require_positive_open_diagonals()
Every OPEN origin’s latest-diagonal cell must be strictly positive.
Usage
require_positive_open_diagonals()The factor estimator cannot enforce this and never could. Its c0 cells are exactly the cells with an observed successor, and on a run-off staircase an open origin’s diagonal cell has none - so the diagonal is the one cell class no factor-side guard ever sees. Every variance formula downstream then divides by it: msep_runoff’s process term (ratio_j / C-hat_{i,j} starting at j = latest_dev[i]) and Merz-Wuthrich’s Phi_i = ratio_k / C_{i,k} + ....
numpy divides silently, so without this the failure is invisible: a zero diagonal returns NaN msep for that origin AND a NaN total, a negative one returns a finite NEGATIVE msep whose square root is then NaN, and simulate_ultimates returns an exactly degenerate zero column because var = sigma2 * state is non-positive and every draw comes back at its mean. Nothing raises; the numbers are just wrong.
Deliberately NOT called on the point path. The chain-ladder ultimate is a product of factors off that cell and needs no positivity at all, and the gallery’s skill benchmark (scripts/compare_gallery.py) wants the ultimate even for a cohort whose variance is undefined. fit_mack therefore still succeeds; only msep_runoff / simulate_ultimates / the CDR refuse.
A CLOSED origin (already at the last dev column) is exempt: it has no remaining step, so nothing divides by its diagonal.
summary()
One row per origin: latest, ultimate, IBNR and the run-off standard
Usage
summary()error, plus a total row. Mirrors cl.MackChainladder.summary_.
to_arrow()
Arrow IPC bytes. Carries n_obs and n_pos separately, because
Usage
to_arrow(*, compression=None)they are different counts and the positivity contract is the difference.