P&C Reserving Model
Published on August 24, 2025
this started out as an end-to-end chain-ladder model on a clean development triangle, working through LDFs, CDFs and IBNR/reserves with python visualizations. from there it turned into ongoing open-source contributions to chainladder-python, the main python library for actuarial reserving (maintained by the CAS).
the work has grown past documentation. it now covers the API reference rendering, the public API surface itself, deprecating stale array backends, and recreating textbook exhibits from Friedland’s Estimating Unpaid Claims Using Basic Techniques as reconciled, doctested examples.
so far i’ve had 18 pull requests merged. the docs work started on an experimental branch, which has since been squashed and promoted into main (twice, most recently via #1126), so everything below now lives on the main line.
documentation and API reference
the goal here was simple: make the core reserving methods readable from the docs alone, without digging through source. if you land on a method, you should see what it does, its signature, and a worked example you can copy.
- #722 - tail constant examples (issue #704) - added worked doctest examples for
TailConstant, so the tail-factor workflow is shown end to end instead of described in the abstract. - #836 - BootstrapODPSample doctest examples - runnable examples for the bootstrap ODP sampler, the method most people reach for when they want a distribution of reserves rather than a point estimate.
- #844 - doctest examples for correlation classes - examples for the development and valuation correlation tests, the diagnostics you run to check whether the chain-ladder assumptions actually hold.
- #848 - README documenting docs build sources and outputs - a contributor-facing README explaining how the docs are built, where the sources live and what each output is, so the next person doesn’t have to reverse-engineer the pipeline.
- #879 - remove autosummary-generated API stubs - cleaned out the auto-generated stub pages that were cluttering the API reference and going stale.
- #885 - render API methods inline via custom autosummary templates - custom sphinx templates so methods render inline on the class page, instead of every method spawning its own thin stub page.
- #900 - bring autosummary templates to parity (issue #847) - brought the remaining templates up to the same standard so the whole API reference renders consistently.
core API
these touch the public surface of the library, the bits users actually import and call, so they were the ones with the most review back-and-forth.
- #901 - add
emptyattribute to Triangle for pandas consistency - gaveTrianglean.emptyattribute that behaves like pandas, so the object feels familiar to anyone already used to dataframes. - #912 - make API exports explicit (issue #876) - made the package exports explicit instead of relying on implicit star-imports, which makes the public API clearer and tooling (autocomplete, linters, type checkers) behave properly.
- #1123 - enable string assignment to axis parameter in Triangle.drop() (issue #1054) - let
axis="columns"work the same asaxis=1, matching pandas’ string-or-int convention instead of silently only accepting integers.
backend deprecation and cleanup
chainladder carried optional gpu and distributed backends that were mostly unused and hard to maintain. removing them cleanly, behind deprecation warnings rather than sudden breakage, keeps the library lighter without surprising existing users.
- #881 - deprecate cupy array backend (issue #843) - started the deprecation of the cupy (gpu) array backend, with warnings so current users get a heads-up before it’s removed.
- #937 - deprecate dask array backend (issue #842) - same treatment for the dask array backend.
- #1008 - deprecate dask parallel-compute (bag) paths (issue #842) - finished the dask deprecation by removing the parallel-compute (bag) code paths, closing out that cleanup.
Friedland textbook recreations
separate from the API work, i’ve been recreating worked exhibits from Jacqueline Friedland’s Estimating Unpaid Claims Using Basic Techniques (the standard CAS reserving text) as reconciled, doctested chainladder-python examples, so the library’s docs double as a way to learn the methods against a source practicing actuaries already trust.
- #1109 - Friedland Chapter 9: Bornhuetter-Ferguson technique - recreated the XYZ Insurer Auto BI exhibits end to end with in-package
Development,TailConstantandBornhuetterFerguson, reconciling ultimates and IBNR to the dollar against the textbook. - #1112 - Chapter 9 follow-up: loc/iloc setter, model persistence, Exhibits I and III - addressed review feedback by wiring up real model persistence between chapters and streamlining the exhibit tables with
cl.model_diagnostics. - #1122 - Chapter 9 follow-up (round 2) - added
pct_reported_/pct_unreported_properties to fitted estimators and further tightened the Exhibit I reconciliation. - #1124 - fix Chapter 9 readthedocs build: JSON instead of pickle for estimator handoff - swapped the chapter-to-chapter model handoff from pickle to chainladder’s own JSON persistence, since pickles don’t survive dependency-version drift across build environments.
what i took from it
the docs work taught me the library inside out, which is what made the later core-API, deprecation and Friedland recreation work possible. reading and reviewing a mature reserving codebase, then changing it in a way maintainers will accept, is a different skill from writing your own model from scratch, and that’s the part i value most here. recreating Friedland’s exhibits added another layer: reconciling a maintained library against a fixed textbook answer forces you to be precise about every rounding and estimator choice along the way.
moving forward
i’m keeping at it, working toward a more complete, production-ready chainladder-python. that means more test coverage across the reserving methods, finishing the backend cleanup, working through more Friedland chapters, and getting deeper into the modelling internals rather than the edges.

