ratingmodels 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. ratingmodels-0.1.0/.gitignore +66 -0
  2. ratingmodels-0.1.0/CHANGELOG.md +55 -0
  3. ratingmodels-0.1.0/LICENSE +21 -0
  4. ratingmodels-0.1.0/PKG-INFO +230 -0
  5. ratingmodels-0.1.0/README.md +198 -0
  6. ratingmodels-0.1.0/docs/api/base_loading.md +5 -0
  7. ratingmodels-0.1.0/docs/api/buildup.md +3 -0
  8. ratingmodels-0.1.0/docs/api/credibility.md +3 -0
  9. ratingmodels-0.1.0/docs/api/decomposition.md +3 -0
  10. ratingmodels-0.1.0/docs/api/indication.md +5 -0
  11. ratingmodels-0.1.0/docs/api/rates.md +5 -0
  12. ratingmodels-0.1.0/docs/api/relativity.md +3 -0
  13. ratingmodels-0.1.0/docs/api/renewal.md +5 -0
  14. ratingmodels-0.1.0/docs/api/trend.md +3 -0
  15. ratingmodels-0.1.0/docs/changelog.md +55 -0
  16. ratingmodels-0.1.0/docs/index.md +66 -0
  17. ratingmodels-0.1.0/docs/theory.md +227 -0
  18. ratingmodels-0.1.0/examples/quickstart.py +154 -0
  19. ratingmodels-0.1.0/mkdocs.yml +68 -0
  20. ratingmodels-0.1.0/pyproject.toml +58 -0
  21. ratingmodels-0.1.0/src/ratingmodels/__init__.py +157 -0
  22. ratingmodels-0.1.0/src/ratingmodels/_utils.py +53 -0
  23. ratingmodels-0.1.0/src/ratingmodels/base_rate.py +151 -0
  24. ratingmodels-0.1.0/src/ratingmodels/blend.py +20 -0
  25. ratingmodels-0.1.0/src/ratingmodels/buildup.py +253 -0
  26. ratingmodels-0.1.0/src/ratingmodels/constraints.py +58 -0
  27. ratingmodels-0.1.0/src/ratingmodels/credibility.py +129 -0
  28. ratingmodels-0.1.0/src/ratingmodels/datasets.py +78 -0
  29. ratingmodels-0.1.0/src/ratingmodels/decomposition.py +102 -0
  30. ratingmodels-0.1.0/src/ratingmodels/experience_rate.py +131 -0
  31. ratingmodels-0.1.0/src/ratingmodels/indication.py +156 -0
  32. ratingmodels-0.1.0/src/ratingmodels/loading.py +126 -0
  33. ratingmodels-0.1.0/src/ratingmodels/manual_rate.py +110 -0
  34. ratingmodels-0.1.0/src/ratingmodels/relativity.py +300 -0
  35. ratingmodels-0.1.0/src/ratingmodels/renewal.py +84 -0
  36. ratingmodels-0.1.0/src/ratingmodels/trend.py +74 -0
  37. ratingmodels-0.1.0/tests/test_ratingmodels.py +590 -0
@@ -0,0 +1,66 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[cod]
4
+ *$py.class
5
+
6
+ # Python build artifacts
7
+ build/
8
+ dist/
9
+ *.egg-info/
10
+ .eggs/
11
+ *.egg
12
+
13
+ # Virtual environments
14
+ .venv/
15
+ venv/
16
+ env/
17
+ ENV/
18
+
19
+ # Test and coverage outputs
20
+ .pytest_cache/
21
+ .coverage
22
+ coverage.xml
23
+ htmlcov/
24
+ .tox/
25
+ .nox/
26
+
27
+ # Type checker / linter caches
28
+ .mypy_cache/
29
+ .pyright/
30
+ .ruff_cache/
31
+
32
+ # Jupyter notebooks
33
+ .ipynb_checkpoints/
34
+
35
+ # Local environment files
36
+ .env
37
+ .env.*
38
+ *.local
39
+
40
+ # IDE / editor files
41
+ .vscode/
42
+ .idea/
43
+ *.swp
44
+ *.swo
45
+ .DS_Store
46
+
47
+ # Logs
48
+ *.log
49
+
50
+ # Temporary files
51
+ tmp/
52
+ temp/
53
+ *.tmp
54
+
55
+ # Data files generated locally
56
+ *.csv
57
+ *.xlsx
58
+ *.xls
59
+ *.parquet
60
+ *.feather
61
+ *.pkl
62
+ *.pickle
63
+
64
+ # Keep example datasets only if intentionally committed
65
+ # !examples/*.csv
66
+ # !examples/*.xlsx
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/), and the project adheres to
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-06-29
8
+
9
+ Initial release.
10
+
11
+ ### Changed
12
+ - **Credibility consolidated into `actuarialpy`.** Trend and credibility are
13
+ shared ecosystem primitives; `actuarialpy` is the home for credibility. The
14
+ credibility math is no longer duplicated here -- `ratingmodels.credibility`
15
+ and `blend` are thin adapters over `actuarialpy` (`full_credibility_claims`,
16
+ `limited_fluctuation_z`, `credibility_weighted_estimate`, and
17
+ `BuhlmannStraub.from_frame`). The public `ratingmodels` API and results are
18
+ unchanged. This removes the risk of the two Bühlmann-Straub implementations
19
+ drifting apart, and `actuarialpy` now uses the general unbiased estimator
20
+ (handling unequal period counts). Adds a dependency on `actuarialpy>=0.32.0`
21
+ and drops the direct `scipy` dependency.
22
+
23
+ ### Added
24
+ - **Credibility** (`credibility`): `full_credibility_standard`,
25
+ `limited_fluctuation_credibility`, `buhlmann_credibility`, and empirical
26
+ `buhlmann_straub` with exposure weights.
27
+ - **Trend** (`trend`): midpoint-to-midpoint trend factors, date helpers, and
28
+ utilization / unit-cost decomposition.
29
+ - **Manual rating** (`manual_rate`): `ManualRate`, `manual_pmpm`,
30
+ `aggregate_demographic_factor`.
31
+ - **Experience rating** (`experience_rate`): `ExperienceRate`, `pool_claims`,
32
+ `expected_excess_charge`.
33
+ - **Rate build-up** (`buildup`): typed steps (`start`, `multiply`, `add`,
34
+ `segment_multiply`, `checkpoint`), an `evaluate` engine and `BuildUp` fluent
35
+ builder producing a reconciling breakdown, and `participation_blend` /
36
+ `combine_streams` for combining par/non-par and medical+drug streams.
37
+ `ManualRate` is reimplemented as a thin layer over the engine and gains
38
+ `breakdown()` / `steps()`.
39
+ - **Base rate & off-balance** (`base_rate`): `base_rate_from_experience`,
40
+ `average_relativity`, `off_balance_factor`, `rebalance_base_rate`.
41
+ - **Retention & loading** (`loading`): `RetentionLoad` (fundamental insurance
42
+ equation gross-up), `gross_rate`, `permissible_loss_ratio`. `ManualRate`,
43
+ `ExperienceRate`, and `RateIndication` accept an optional `retention` that
44
+ overrides the single-loss-ratio path with the full expense/profit build-up.
45
+ - **Blending & indication** (`blend`, `indication`): `blend` and the
46
+ `RateIndication` orchestrator with build-up and loss-ratio methods.
47
+ - **Rate-change decomposition** (`decomposition`): `decompose_rate_change`
48
+ with multiplicative and percentage-point contributions and an explicit
49
+ residual.
50
+ - **GLM relativities** (`relativity`): `GLMRelativities` (Poisson / Gamma /
51
+ Tweedie via in-package IRLS), `FactorTable`, `one_way_relativities`.
52
+ - **Constraints & renewal** (`constraints`, `renewal`): caps, floors, banding,
53
+ rounding, corridors; `renew` and `member_level_renewal`.
54
+ - **Synthetic data** (`datasets`): `sample_claims`, `sample_rating_data`.
55
+ - Full pytest suite (54 tests) and MkDocs Material documentation.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 OpenActuarial
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,230 @@
1
+ Metadata-Version: 2.4
2
+ Name: ratingmodels
3
+ Version: 0.1.0
4
+ Summary: Actuarial pricing and rate-indication tools for experience-rated insurance portfolios.
5
+ Project-URL: Homepage, https://github.com/OpenActuarial/ratingmodels
6
+ Project-URL: Documentation, https://openactuarial.github.io/ratingmodels/
7
+ Project-URL: Repository, https://github.com/OpenActuarial/ratingmodels
8
+ Project-URL: Issues, https://github.com/OpenActuarial/ratingmodels/issues
9
+ Author: OpenActuarial
10
+ License: MIT
11
+ License-File: LICENSE
12
+ Keywords: actuarial,credibility,glm,health-insurance,insurance,pricing,rate-indication,rating
13
+ Classifier: Development Status :: 4 - Beta
14
+ Classifier: Intended Audience :: Financial and Insurance Industry
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: License :: OSI Approved :: MIT License
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3 :: Only
19
+ Classifier: Topic :: Office/Business :: Financial
20
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
21
+ Requires-Python: >=3.9
22
+ Requires-Dist: actuarialpy>=0.32.0
23
+ Requires-Dist: numpy>=1.22
24
+ Requires-Dist: pandas>=1.4
25
+ Provides-Extra: dev
26
+ Requires-Dist: pytest-cov; extra == 'dev'
27
+ Requires-Dist: pytest>=7; extra == 'dev'
28
+ Provides-Extra: docs
29
+ Requires-Dist: mkdocs-material>=9; extra == 'docs'
30
+ Requires-Dist: mkdocstrings[python]>=0.24; extra == 'docs'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # ratingmodels
34
+
35
+ **Actuarial pricing and rate-indication tools for experience-rated insurance portfolios.**
36
+
37
+ Part of the [OpenActuarial](https://github.com/OpenActuarial) ecosystem.
38
+
39
+ `ratingmodels` covers the group rating workflow — the step that turns experience
40
+ analysis and loss modeling into an actual rate. Where the rest of the ecosystem
41
+ explains *what happened* and models *loss risk*, this package answers the central
42
+ pricing question: **what rate should we charge, and why did it change?**
43
+
44
+ ```
45
+ actuarialpy -> experience analysis (PMPM, loss ratios, trend, completion)
46
+ ratingmodels -> pricing and rate indications <-- this package
47
+ lossmodels -> loss-distribution modeling
48
+ risksim -> portfolio Monte Carlo simulation
49
+ extremeloss -> extreme-value tail estimation
50
+ ```
51
+
52
+ ## What it does
53
+
54
+ - **Credibility** — limited fluctuation (square-root rule), Bühlmann, and
55
+ empirical Bühlmann-Straub with exposure weights.
56
+ - **Trend** — midpoint-to-midpoint factors; utilization / unit-cost split.
57
+ - **Manual rating** — base rate × relativities, loaded to a charged rate.
58
+ - **Experience rating** — pooling of large claims, trend, pooling charge,
59
+ benefit/demographic adjustments, loading.
60
+ - **Rate build-up** — an ordered, auditable evaluator (multiply / add-dollar /
61
+ segment-conditional) with labeled subtotals and a reconciling breakdown, plus
62
+ par/non-par participation blending and medical+drug combining. Supplies the
63
+ *grammar* of a manual build-up; the factor values stay yours.
64
+ - **Base rate & off-balance** — indicated base loss cost from book experience
65
+ (base × relativities reproduces book losses); off-balance correction and
66
+ base rebalancing when relativities are revised.
67
+ - **Retention & gross-up** — charged rate from the fundamental insurance
68
+ equation (loss & LAE, flat fixed expense, percent-of-premium loads, profit),
69
+ with the target loss ratio as an *output*, not an input.
70
+ - **Blending & indication** — credibility-weighted blend; build-up and
71
+ loss-ratio indication methods.
72
+ - **Rate-change decomposition** — multiplicative and percentage-point
73
+ contribution-to-change with an explicit residual.
74
+ - **GLM relativities** — Poisson / Gamma / Tweedie GLMs fit by IRLS, so factors
75
+ are estimated *jointly* (correcting for correlation between rating variables)
76
+ rather than one-way. No statsmodels dependency — the IRLS is in-package.
77
+ - **Constraints & renewal** — rate caps/floors, banding, rounding, corridors,
78
+ and member-level re-rating.
79
+
80
+ Dependencies are `numpy`, `pandas`, and `actuarialpy` (which supplies the shared credibility primitives; see below).
81
+
82
+ ## Install
83
+
84
+ ```bash
85
+ pip install ratingmodels
86
+ ```
87
+
88
+ From source:
89
+
90
+ ```bash
91
+ git clone https://github.com/OpenActuarial/ratingmodels
92
+ cd ratingmodels
93
+ pip install -e ".[dev]"
94
+ pytest
95
+ ```
96
+
97
+ ## Quick start
98
+
99
+ ```python
100
+ import ratingmodels as rm
101
+
102
+ # --- experience side -------------------------------------------------------
103
+ capped, excess = rm.pool_claims(group_claims, pooling_point=250_000)
104
+ exp = rm.ExperienceRate(
105
+ incurred_claims=4_200_000,
106
+ exposure=96_000, # member-months
107
+ trend_annual=0.075,
108
+ trend_years=1.5, # experience midpoint -> rating midpoint
109
+ pooled_excess=excess,
110
+ pooling_charge_pmpm=4.00,
111
+ target_loss_ratio=0.85,
112
+ )
113
+
114
+ # --- manual side -----------------------------------------------------------
115
+ man = rm.ManualRate(
116
+ base_pmpm=480,
117
+ factors={"area": 1.05, "industry": 0.97, "tier": 1.10},
118
+ target_loss_ratio=0.85,
119
+ )
120
+
121
+ # --- credibility and indication -------------------------------------------
122
+ z = rm.limited_fluctuation_credibility(n=96_000, n_full=120_000)
123
+
124
+ ind = rm.RateIndication(
125
+ experience_claims_pmpm=exp.claims_pmpm(),
126
+ manual_claims_pmpm=man.claims_pmpm(),
127
+ credibility=z,
128
+ current_rate=560,
129
+ target_loss_ratio=0.85,
130
+ trend_total_factor=exp.trend_factor(),
131
+ benefit_factor=1.00,
132
+ demographic_factor=1.01,
133
+ )
134
+
135
+ print(f"indicated rate : {ind.indicated_rate():.2f}")
136
+ print(f"indicated change : {ind.indicated_rate_change():+.2%}")
137
+
138
+ # why did the rate move?
139
+ print(ind.rate_change_decomposition().to_frame())
140
+
141
+ # apply a renewal cap
142
+ action = rm.renew(current_rate=560, indicated_rate=ind.indicated_rate(), cap=0.15)
143
+ print(f"proposed (capped): {action.proposed_rate:.2f} ({action.proposed_change:+.2%})")
144
+ ```
145
+
146
+ ### Rate build-up with an audit trail
147
+
148
+ ```python
149
+ import ratingmodels as rm
150
+
151
+ med_par = rm.evaluate([
152
+ rm.start("Par Base Claim Cost", 941.63),
153
+ rm.add("$30 specialist copay", -11.44),
154
+ rm.multiply("Rating Region", 1.083),
155
+ rm.checkpoint("Medical Par Base Claim Cost"),
156
+ ])
157
+ med_par.value # final running total
158
+ med_par.breakdown # DataFrame: step, operation, label, operand, running_total
159
+
160
+ # blend in-/out-of-network, then add the drug stream
161
+ med = rm.participation_blend(med_par.value, nonpar=1478.56, participation_rate=0.90)
162
+ total = rm.combine_streams({"Medical": med, "Drug": 323.67})
163
+ total.value # feeds into trend / credibility / retention
164
+ ```
165
+
166
+ The package supplies the build-up *grammar*; you supply the factor values
167
+ (cost-sharing, age/sex, area, ...) from your filed tables. `ManualRate` is a
168
+ thin shortcut over this engine, so `ManualRate(...).breakdown()` returns the
169
+ same audit trail.
170
+
171
+ ### Base rate and retention
172
+
173
+ ```python
174
+ import ratingmodels as rm
175
+ import pandas as pd
176
+
177
+ # indicated base loss cost from book experience (off-balance method)
178
+ book = pd.DataFrame({
179
+ "exposure": [24_000, 18_000, 30_000, 24_000],
180
+ "area": [1.00, 1.20, 0.90, 1.05],
181
+ "tier": [1.00, 1.10, 0.95, 1.25],
182
+ "loss": [11_750_000, 11_400_000, 12_300_000, 15_200_000],
183
+ })
184
+ base = rm.base_rate_from_experience(book, "exposure", "loss",
185
+ factor_cols=["area", "tier"])
186
+ base.base_loss_cost # average loss cost / average relativity
187
+
188
+ # gross claims up to a charged rate; the loss ratio falls out
189
+ retention = rm.RetentionLoad.from_items(
190
+ fixed_expense_pmpm=22.0,
191
+ variable_items={"commission": 0.03, "premium_tax": 0.023, "aca_fees": 0.005},
192
+ profit_margin=0.03,
193
+ )
194
+ retention.gross_rate(540.0) # charged rate
195
+ retention.implied_loss_ratio(540.0) # target loss ratio (an output)
196
+
197
+ # rebalance the base when relativities are revised (hold level, then +8%)
198
+ rm.rebalance_base_rate(current_base=base.base_loss_cost,
199
+ current_avg_relativity=1.0928, new_avg_relativity=1.12,
200
+ overall_change=0.08)
201
+ ```
202
+
203
+ ### GLM relativities
204
+
205
+ ```python
206
+ import ratingmodels as rm
207
+ from ratingmodels.datasets import sample_rating_data
208
+
209
+ df = sample_rating_data(n=20_000)
210
+ model = rm.GLMRelativities(family="poisson").fit(
211
+ df, response="claims", predictors=["area", "industry", "tier"],
212
+ exposure="exposure", # enters as a log offset
213
+ base_levels={"area": "A"}, # optional; defaults to modal level
214
+ )
215
+ print(model.base_value_) # fitted base frequency
216
+ print(model.relativities_["industry"]) # relativity per level, base = 1.0
217
+ ```
218
+
219
+ ## Scope and honest limitations
220
+
221
+ This is a modeling and workflow toolkit, not filed rate software. It does not
222
+ manage rate filings, store filed factor tables with effective dating, or enforce
223
+ state-specific rating rules. The pooling-charge helper is a simple group-level
224
+ estimate; a production charge is normally derived book-wide or from an EVT tail
225
+ model (see `extremeloss`). All bundled data in `ratingmodels.datasets` is
226
+ synthetic and carries no assumptions.
227
+
228
+ ## License
229
+
230
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,198 @@
1
+ # ratingmodels
2
+
3
+ **Actuarial pricing and rate-indication tools for experience-rated insurance portfolios.**
4
+
5
+ Part of the [OpenActuarial](https://github.com/OpenActuarial) ecosystem.
6
+
7
+ `ratingmodels` covers the group rating workflow — the step that turns experience
8
+ analysis and loss modeling into an actual rate. Where the rest of the ecosystem
9
+ explains *what happened* and models *loss risk*, this package answers the central
10
+ pricing question: **what rate should we charge, and why did it change?**
11
+
12
+ ```
13
+ actuarialpy -> experience analysis (PMPM, loss ratios, trend, completion)
14
+ ratingmodels -> pricing and rate indications <-- this package
15
+ lossmodels -> loss-distribution modeling
16
+ risksim -> portfolio Monte Carlo simulation
17
+ extremeloss -> extreme-value tail estimation
18
+ ```
19
+
20
+ ## What it does
21
+
22
+ - **Credibility** — limited fluctuation (square-root rule), Bühlmann, and
23
+ empirical Bühlmann-Straub with exposure weights.
24
+ - **Trend** — midpoint-to-midpoint factors; utilization / unit-cost split.
25
+ - **Manual rating** — base rate × relativities, loaded to a charged rate.
26
+ - **Experience rating** — pooling of large claims, trend, pooling charge,
27
+ benefit/demographic adjustments, loading.
28
+ - **Rate build-up** — an ordered, auditable evaluator (multiply / add-dollar /
29
+ segment-conditional) with labeled subtotals and a reconciling breakdown, plus
30
+ par/non-par participation blending and medical+drug combining. Supplies the
31
+ *grammar* of a manual build-up; the factor values stay yours.
32
+ - **Base rate & off-balance** — indicated base loss cost from book experience
33
+ (base × relativities reproduces book losses); off-balance correction and
34
+ base rebalancing when relativities are revised.
35
+ - **Retention & gross-up** — charged rate from the fundamental insurance
36
+ equation (loss & LAE, flat fixed expense, percent-of-premium loads, profit),
37
+ with the target loss ratio as an *output*, not an input.
38
+ - **Blending & indication** — credibility-weighted blend; build-up and
39
+ loss-ratio indication methods.
40
+ - **Rate-change decomposition** — multiplicative and percentage-point
41
+ contribution-to-change with an explicit residual.
42
+ - **GLM relativities** — Poisson / Gamma / Tweedie GLMs fit by IRLS, so factors
43
+ are estimated *jointly* (correcting for correlation between rating variables)
44
+ rather than one-way. No statsmodels dependency — the IRLS is in-package.
45
+ - **Constraints & renewal** — rate caps/floors, banding, rounding, corridors,
46
+ and member-level re-rating.
47
+
48
+ Dependencies are `numpy`, `pandas`, and `actuarialpy` (which supplies the shared credibility primitives; see below).
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install ratingmodels
54
+ ```
55
+
56
+ From source:
57
+
58
+ ```bash
59
+ git clone https://github.com/OpenActuarial/ratingmodels
60
+ cd ratingmodels
61
+ pip install -e ".[dev]"
62
+ pytest
63
+ ```
64
+
65
+ ## Quick start
66
+
67
+ ```python
68
+ import ratingmodels as rm
69
+
70
+ # --- experience side -------------------------------------------------------
71
+ capped, excess = rm.pool_claims(group_claims, pooling_point=250_000)
72
+ exp = rm.ExperienceRate(
73
+ incurred_claims=4_200_000,
74
+ exposure=96_000, # member-months
75
+ trend_annual=0.075,
76
+ trend_years=1.5, # experience midpoint -> rating midpoint
77
+ pooled_excess=excess,
78
+ pooling_charge_pmpm=4.00,
79
+ target_loss_ratio=0.85,
80
+ )
81
+
82
+ # --- manual side -----------------------------------------------------------
83
+ man = rm.ManualRate(
84
+ base_pmpm=480,
85
+ factors={"area": 1.05, "industry": 0.97, "tier": 1.10},
86
+ target_loss_ratio=0.85,
87
+ )
88
+
89
+ # --- credibility and indication -------------------------------------------
90
+ z = rm.limited_fluctuation_credibility(n=96_000, n_full=120_000)
91
+
92
+ ind = rm.RateIndication(
93
+ experience_claims_pmpm=exp.claims_pmpm(),
94
+ manual_claims_pmpm=man.claims_pmpm(),
95
+ credibility=z,
96
+ current_rate=560,
97
+ target_loss_ratio=0.85,
98
+ trend_total_factor=exp.trend_factor(),
99
+ benefit_factor=1.00,
100
+ demographic_factor=1.01,
101
+ )
102
+
103
+ print(f"indicated rate : {ind.indicated_rate():.2f}")
104
+ print(f"indicated change : {ind.indicated_rate_change():+.2%}")
105
+
106
+ # why did the rate move?
107
+ print(ind.rate_change_decomposition().to_frame())
108
+
109
+ # apply a renewal cap
110
+ action = rm.renew(current_rate=560, indicated_rate=ind.indicated_rate(), cap=0.15)
111
+ print(f"proposed (capped): {action.proposed_rate:.2f} ({action.proposed_change:+.2%})")
112
+ ```
113
+
114
+ ### Rate build-up with an audit trail
115
+
116
+ ```python
117
+ import ratingmodels as rm
118
+
119
+ med_par = rm.evaluate([
120
+ rm.start("Par Base Claim Cost", 941.63),
121
+ rm.add("$30 specialist copay", -11.44),
122
+ rm.multiply("Rating Region", 1.083),
123
+ rm.checkpoint("Medical Par Base Claim Cost"),
124
+ ])
125
+ med_par.value # final running total
126
+ med_par.breakdown # DataFrame: step, operation, label, operand, running_total
127
+
128
+ # blend in-/out-of-network, then add the drug stream
129
+ med = rm.participation_blend(med_par.value, nonpar=1478.56, participation_rate=0.90)
130
+ total = rm.combine_streams({"Medical": med, "Drug": 323.67})
131
+ total.value # feeds into trend / credibility / retention
132
+ ```
133
+
134
+ The package supplies the build-up *grammar*; you supply the factor values
135
+ (cost-sharing, age/sex, area, ...) from your filed tables. `ManualRate` is a
136
+ thin shortcut over this engine, so `ManualRate(...).breakdown()` returns the
137
+ same audit trail.
138
+
139
+ ### Base rate and retention
140
+
141
+ ```python
142
+ import ratingmodels as rm
143
+ import pandas as pd
144
+
145
+ # indicated base loss cost from book experience (off-balance method)
146
+ book = pd.DataFrame({
147
+ "exposure": [24_000, 18_000, 30_000, 24_000],
148
+ "area": [1.00, 1.20, 0.90, 1.05],
149
+ "tier": [1.00, 1.10, 0.95, 1.25],
150
+ "loss": [11_750_000, 11_400_000, 12_300_000, 15_200_000],
151
+ })
152
+ base = rm.base_rate_from_experience(book, "exposure", "loss",
153
+ factor_cols=["area", "tier"])
154
+ base.base_loss_cost # average loss cost / average relativity
155
+
156
+ # gross claims up to a charged rate; the loss ratio falls out
157
+ retention = rm.RetentionLoad.from_items(
158
+ fixed_expense_pmpm=22.0,
159
+ variable_items={"commission": 0.03, "premium_tax": 0.023, "aca_fees": 0.005},
160
+ profit_margin=0.03,
161
+ )
162
+ retention.gross_rate(540.0) # charged rate
163
+ retention.implied_loss_ratio(540.0) # target loss ratio (an output)
164
+
165
+ # rebalance the base when relativities are revised (hold level, then +8%)
166
+ rm.rebalance_base_rate(current_base=base.base_loss_cost,
167
+ current_avg_relativity=1.0928, new_avg_relativity=1.12,
168
+ overall_change=0.08)
169
+ ```
170
+
171
+ ### GLM relativities
172
+
173
+ ```python
174
+ import ratingmodels as rm
175
+ from ratingmodels.datasets import sample_rating_data
176
+
177
+ df = sample_rating_data(n=20_000)
178
+ model = rm.GLMRelativities(family="poisson").fit(
179
+ df, response="claims", predictors=["area", "industry", "tier"],
180
+ exposure="exposure", # enters as a log offset
181
+ base_levels={"area": "A"}, # optional; defaults to modal level
182
+ )
183
+ print(model.base_value_) # fitted base frequency
184
+ print(model.relativities_["industry"]) # relativity per level, base = 1.0
185
+ ```
186
+
187
+ ## Scope and honest limitations
188
+
189
+ This is a modeling and workflow toolkit, not filed rate software. It does not
190
+ manage rate filings, store filed factor tables with effective dating, or enforce
191
+ state-specific rating rules. The pooling-charge helper is a simple group-level
192
+ estimate; a production charge is normally derived book-wide or from an EVT tail
193
+ model (see `extremeloss`). All bundled data in `ratingmodels.datasets` is
194
+ synthetic and carries no assumptions.
195
+
196
+ ## License
197
+
198
+ MIT. See [LICENSE](LICENSE).
@@ -0,0 +1,5 @@
1
+ # Base rate & retention
2
+
3
+ ::: ratingmodels.base_rate
4
+
5
+ ::: ratingmodels.loading
@@ -0,0 +1,3 @@
1
+ # Build-up engine
2
+
3
+ ::: ratingmodels.buildup
@@ -0,0 +1,3 @@
1
+ # Credibility
2
+
3
+ ::: ratingmodels.credibility
@@ -0,0 +1,3 @@
1
+ # Rate-change decomposition
2
+
3
+ ::: ratingmodels.decomposition
@@ -0,0 +1,5 @@
1
+ # Indication & blending
2
+
3
+ ::: ratingmodels.indication
4
+
5
+ ::: ratingmodels.blend
@@ -0,0 +1,5 @@
1
+ # Manual & experience rating
2
+
3
+ ::: ratingmodels.manual_rate
4
+
5
+ ::: ratingmodels.experience_rate
@@ -0,0 +1,3 @@
1
+ # Relativities (GLM)
2
+
3
+ ::: ratingmodels.relativity
@@ -0,0 +1,5 @@
1
+ # Constraints & renewal
2
+
3
+ ::: ratingmodels.constraints
4
+
5
+ ::: ratingmodels.renewal
@@ -0,0 +1,3 @@
1
+ # Trend
2
+
3
+ ::: ratingmodels.trend
@@ -0,0 +1,55 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows
4
+ [Keep a Changelog](https://keepachangelog.com/), and the project adheres to
5
+ [Semantic Versioning](https://semver.org/).
6
+
7
+ ## [0.1.0] - 2026-06-29
8
+
9
+ Initial release.
10
+
11
+ ### Changed
12
+ - **Credibility consolidated into `actuarialpy`.** Trend and credibility are
13
+ shared ecosystem primitives; `actuarialpy` is the home for credibility. The
14
+ credibility math is no longer duplicated here -- `ratingmodels.credibility`
15
+ and `blend` are thin adapters over `actuarialpy` (`full_credibility_claims`,
16
+ `limited_fluctuation_z`, `credibility_weighted_estimate`, and
17
+ `BuhlmannStraub.from_frame`). The public `ratingmodels` API and results are
18
+ unchanged. This removes the risk of the two Bühlmann-Straub implementations
19
+ drifting apart, and `actuarialpy` now uses the general unbiased estimator
20
+ (handling unequal period counts). Adds a dependency on `actuarialpy>=0.32.0`
21
+ and drops the direct `scipy` dependency.
22
+
23
+ ### Added
24
+ - **Credibility** (`credibility`): `full_credibility_standard`,
25
+ `limited_fluctuation_credibility`, `buhlmann_credibility`, and empirical
26
+ `buhlmann_straub` with exposure weights.
27
+ - **Trend** (`trend`): midpoint-to-midpoint trend factors, date helpers, and
28
+ utilization / unit-cost decomposition.
29
+ - **Manual rating** (`manual_rate`): `ManualRate`, `manual_pmpm`,
30
+ `aggregate_demographic_factor`.
31
+ - **Experience rating** (`experience_rate`): `ExperienceRate`, `pool_claims`,
32
+ `expected_excess_charge`.
33
+ - **Rate build-up** (`buildup`): typed steps (`start`, `multiply`, `add`,
34
+ `segment_multiply`, `checkpoint`), an `evaluate` engine and `BuildUp` fluent
35
+ builder producing a reconciling breakdown, and `participation_blend` /
36
+ `combine_streams` for combining par/non-par and medical+drug streams.
37
+ `ManualRate` is reimplemented as a thin layer over the engine and gains
38
+ `breakdown()` / `steps()`.
39
+ - **Base rate & off-balance** (`base_rate`): `base_rate_from_experience`,
40
+ `average_relativity`, `off_balance_factor`, `rebalance_base_rate`.
41
+ - **Retention & loading** (`loading`): `RetentionLoad` (fundamental insurance
42
+ equation gross-up), `gross_rate`, `permissible_loss_ratio`. `ManualRate`,
43
+ `ExperienceRate`, and `RateIndication` accept an optional `retention` that
44
+ overrides the single-loss-ratio path with the full expense/profit build-up.
45
+ - **Blending & indication** (`blend`, `indication`): `blend` and the
46
+ `RateIndication` orchestrator with build-up and loss-ratio methods.
47
+ - **Rate-change decomposition** (`decomposition`): `decompose_rate_change`
48
+ with multiplicative and percentage-point contributions and an explicit
49
+ residual.
50
+ - **GLM relativities** (`relativity`): `GLMRelativities` (Poisson / Gamma /
51
+ Tweedie via in-package IRLS), `FactorTable`, `one_way_relativities`.
52
+ - **Constraints & renewal** (`constraints`, `renewal`): caps, floors, banding,
53
+ rounding, corridors; `renew` and `member_level_renewal`.
54
+ - **Synthetic data** (`datasets`): `sample_claims`, `sample_rating_data`.
55
+ - Full pytest suite (54 tests) and MkDocs Material documentation.