Skip to content

[FEAT] Add ImpliedCaseDevelopment estimator - #1473

Open
priyam0k wants to merge 5 commits into
mainfrom
feature/issue-282-case-outstanding-approach-2
Open

priyam0k wants to merge 5 commits into
mainfrom
feature/issue-282-case-outstanding-approach-2

Conversation

@priyam0k

@priyam0k priyam0k commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Summary of Changes

Implements Approach 2 of the Case Outstanding method from Friedland (2010, Chapter 12, Exhibit III):

  • Adds ImpliedCaseDevelopment (and ImpliedCase alias) as a standalone estimator deriving implied case development factors from benchmark reported and paid patterns.
  • Keeps CaseOutstanding focused strictly on empirical historical triangle development (Approach 1).
  • Includes unit tests reconciling Friedland Chapter 12 Exhibit III and public API exports.

Related GitHub Issue(s)

Closes #282

AI/LLM Usage

Used basic model for drafting initial unit test cases.

Additional Context for Reviewers

Follows discussion in #282 with @henrydingliu and @kennethshsu decoupling Approach 2 into a standalone estimator, analogous to Development vs DevelopmentConstant.

Submitter's Checklist

  • I have reviewed and am adhering to the standards outlined in the project Governing Doc.
  • The PR subject title summarizes the changes, with one proper prefix ([FIX], [FEAT], [DOCS], [TST], [CHORE], or [BRK]).
  • I am a human (not a bot), and this PR form is written by a human.

Note

Medium Risk
New actuarial development logic affects reserve projection paths; behavior is guarded by input validation and Friedland reconciliation tests but still warrants actuarial review.

Overview
Adds ImpliedCaseDevelopment (and ImpliedCase alias) as a standalone development estimator for Friedland “Approach 2”: implied case CDFs/LDFs are computed from benchmark reported and paid patterns (CDF or LDF via style), then wired through DevelopmentConstant so Chainladder and similar estimators can consume ldf_ on a single-channel case triangle.

CaseOutstanding remains the empirical triangle-based method (Approach 1); its docs now point to the new class for benchmark-pattern workflows, with small doc/param clarifications (e.g. groupby).

Exports and API docs are updated; tests cover Exhibit III numerics, pattern input types, validation, transform/Chainladder integration, and that Approach 2 kwargs are not accepted on CaseOutstanding.

Reviewed by Cursor Bugbot for commit 8221ddd. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Pyright Type Completeness

View the full pyright --verifytypes output for this commit

Project (full chainladder package, at this PR's head): 15.6% of exported symbols fully typed (231 / 1479)

Known Ambiguous Unknown Total
Project (head) 231 108 1140 1479

Other symbols referenced but not exported by chainladder: 13

Known Ambiguous Unknown Total
Other (head) 3 1 9 13

Symbols without documentation:

  • Functions without docstring: 333
  • Functions without default param: 0
  • Classes without docstring: 8

Patch (exported symbols added or changed by this PR): 17.4% fully typed (4 / 23)

Known Ambiguous Unknown Total
Patch 4 0 19 23
Patch symbol details
Symbol Status Change
chainladder.development.ImpliedCase ❌ unknown new
chainladder.development.ImpliedCaseDevelopment ❌ unknown new
chainladder.development.outstanding.ImpliedCase ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.X_ ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.__init__ ✅ known new
chainladder.development.outstanding.ImpliedCaseDevelopment.case_ ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.case_cdf_ ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.case_ldf_ ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.fit ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.ldf_ ❌ unknown new
chainladder.development.outstanding.ImpliedCaseDevelopment.paid_pattern ✅ known new
chainladder.development.outstanding.ImpliedCaseDevelopment.reported_pattern ✅ known new
chainladder.development.outstanding.ImpliedCaseDevelopment.style ✅ known new
chainladder.development.outstanding.ImpliedCaseDevelopment.transform ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_estimator_with_style_ldf_and_no_double_cumprod ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_friedland_exhibit_iii ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_pattern_input_types ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_single_channel_case_triangle_and_transform ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_validation_errors ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_with_development_estimator ❌ unknown new
chainladder.development.tests.test_outstanding.test_approach_2_with_ldf_style ❌ unknown new
chainladder.development.tests.test_outstanding.test_implied_case_development_alias ❌ unknown new

@codecov

codecov Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.43590% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.56%. Comparing base (d046ca3) to head (8221ddd).
⚠️ Report is 9 commits behind head on main.

Files with missing lines Patch % Lines
chainladder/development/outstanding.py 97.40% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1473      +/-   ##
==========================================
+ Coverage   94.14%   94.56%   +0.41%     
==========================================
  Files          96       96              
  Lines        5843     6439     +596     
  Branches      739      892     +153     
==========================================
+ Hits         5501     6089     +588     
- Misses        221      222       +1     
- Partials      121      128       +7     
Flag Coverage Δ
unittests 94.56% <97.43%> (+0.41%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread chainladder/development/outstanding.py Outdated
Comment thread chainladder/development/outstanding.py Outdated

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread chainladder/__init__.py
@priyam0k
priyam0k force-pushed the feature/issue-282-case-outstanding-approach-2 branch from afed399 to 7e7af1f Compare October 7, 2026 17:15

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7e7af1f. Configure here.

Comment thread chainladder/development/outstanding.py Outdated
@priyam0k priyam0k changed the title [FEAT] Add CaseOutstanding Approach 2 and ImpliedCaseDevelopment [FEAT] Add ImpliedCaseDevelopment estimator Oct 9, 2026
@priyam0k
priyam0k marked this pull request as ready for review October 9, 2026 18:42

@henrydingliu henrydingliu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the scope creeping suggestions are optional. feel free to push back and we can leave them to future PRs.

Comment thread chainladder/development/outstanding.py Outdated
Parameters
----------
paid_to_incurred: tuple or list of tuples
paid_to_incurred: tuple or list of tuples, optional

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

borderline out of scope. but, if we are adding "optional" to the docstring, we should describe what the default values are. I'm also not sure if this argument is actually optional

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed , optional to restore exact Approach 1 signature.

Comment thread chainladder/development/outstanding.py Outdated
groupby=None,
**kwargs,
):
if (

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this if we never shipped impliedcase inside caseoutstanding?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed **kwargs and unreleased backward-compatibility error guard.

Comment thread chainladder/development/outstanding.py Outdated
)

# 1. Determine case triangle
if len(X.columns) > 1 and "incurred" in X.columns and "paid" in X.columns:

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since the estimator only makes use of a single column of X, I would argue that the sklearn convention is to only expect X to already contain only case reserves, and leave the preprocessing outside of the estimator.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fit and transform now expect a 1-channel case triangle directly per scikit-learn standard, raising ValueError on multi-channel triangles.

Comment thread chainladder/development/outstanding.py Outdated
self.reported_pattern = reported_pattern
self.paid_pattern = paid_pattern
self.style = style
self.groupby = groupby

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this used?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused groupby parameter from ImpliedCaseDevelopment.__init__.

Comment thread chainladder/development/outstanding.py Outdated
return X_new

@staticmethod
def _extract_cdf_series(pattern, style="cdf"):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a great method. however, i would recommend that it outputs a triangle directly.

Comment thread chainladder/development/outstanding.py Outdated

Attributes
----------
ldf_ : Triangle

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i don't think the ldfs are meaningful in any practical way. certainly it doesn't predict calendar period emergence. i would recommend that we only retain case_cdf_ as the estimated attribute

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented case_cdf_ as primary estimated attribute; retained ldf_ internally for downstream Chainladder compatibility.

Comment thread chainladder/development/outstanding.py Outdated
self,
reported_pattern=None,
paid_pattern=None,
style="cdf",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please add type hinting?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added type hints across __init__, fit, transform, and helper methods.

Comment thread chainladder/development/tests/test_outstanding.py
Comment thread chainladder/development/outstanding.py
Comment thread chainladder/development/outstanding.py
@henrydingliu henrydingliu self-assigned this Oct 9, 2026

@priyam0k priyam0k left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed the review feedback: Left the DevelopmentConstant pattern generalization for a dedicated follow-up issue.

Comment thread chainladder/development/outstanding.py Outdated
groupby=None,
**kwargs,
):
if (

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed **kwargs and unreleased backward-compatibility error guard.

Comment thread chainladder/development/outstanding.py Outdated
)

# 1. Determine case triangle
if len(X.columns) > 1 and "incurred" in X.columns and "paid" in X.columns:

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fit and transform now expect a 1-channel case triangle directly per scikit-learn standard, raising ValueError on multi-channel triangles.

Comment thread chainladder/development/outstanding.py Outdated
Parameters
----------
paid_to_incurred: tuple or list of tuples
paid_to_incurred: tuple or list of tuples, optional

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed , optional to restore exact Approach 1 signature.

Comment thread chainladder/development/outstanding.py Outdated
self.reported_pattern = reported_pattern
self.paid_pattern = paid_pattern
self.style = style
self.groupby = groupby

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed unused groupby parameter from ImpliedCaseDevelopment.__init__.

Comment thread chainladder/development/outstanding.py Outdated
self,
reported_pattern=None,
paid_pattern=None,
style="cdf",

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added type hints across __init__, fit, transform, and helper methods.

Comment thread chainladder/development/outstanding.py
Comment thread chainladder/development/outstanding.py Outdated

Attributes
----------
ldf_ : Triangle

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documented case_cdf_ as primary estimated attribute; retained ldf_ internally for downstream Chainladder compatibility.

Comment thread chainladder/development/tests/test_outstanding.py
Comment thread chainladder/development/outstanding.py
@henrydingliu

Copy link
Copy Markdown
Member

@priyam0k thanks for the revisions. in the future, could you please also click the "re-request review" so I can come back and approve?

image

i also have a slightly out of scope question that I'm going to raise in the original issue.

@priyam0k
priyam0k requested a review from henrydingliu October 11, 2026 19:17
@priyam0k

priyam0k commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor Author

@priyam0k thanks for the revisions. in the future, could you please also click the "re-request review" so I can come back and approve?

Sure, will follow from future. I have re-request for a review

@henrydingliu henrydingliu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a couple of minor comments

self,
reported_pattern: Any = None,
paid_pattern: Any = None,
style: str = "cdf",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could you please update this to Literal["ldf", "cdf"]?

"ImpliedCaseDevelopment requires both reported_pattern and paid_pattern."
)

if len(X.columns) > 1:

@henrydingliu henrydingliu Oct 11, 2026 •

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think this is fine for now. hypothetically, someone could have loss case reserve and alae case reserve as two columns, or net loss case reserves and ceded case reserves as two columns. but to meet a realistic multi-column use case, we'd also have to allow the user to pass in multi-column paid and incurred patterns. today, to do that on DevelopmentConstant is a right pain through callables. once we sufficiently enhance DevelopmentConstant, we can revisit the single column restriction

leaving this comment as evidence that we thought about it and intentionally shipped it this way

self.ldf_ = dev_const.ldf_.set_backend(backend)
self.case_ldf_ = dev_const.ldf_.set_backend(backend)
self.case_cdf_ = dev_const.cdf_.set_backend(backend)
self.case_ = case_tri

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it okay that case_tri is not a copy of X but X itself?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

CaseOutstanding()'s Approach 2 as Proposed by Friedland (2010)

2 participants