catagg 0.1.0.dev0__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.
@@ -0,0 +1,24 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here.
4
+
5
+ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Added
11
+
12
+ - Repository scaffolding generated from the shared template: hatchling build
13
+ with src layout, ruff and mypy configuration, pytest setup, GitHub Actions
14
+ CI (lint, type-check, five-way test matrix, docs build, distribution build),
15
+ MkDocs Material documentation site, MIT licence, issue and pull request
16
+ templates.
17
+ - Empty subpackages fixing the module layout: `io`, `frequency`, `severity`,
18
+ `dependence`, `simulate`, `financial`, `metrics`.
19
+ - Packaging tests: version/metadata drift and `py.typed` installation.
20
+ - Release workflow publishing to PyPI via Trusted Publishing, with a
21
+ tag-versus-package-version guard, and the release procedure documented in
22
+ `docs/releasing.md`.
23
+
24
+ [Unreleased]: https://github.com/John-Amal/catagg/commits/main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Amal John
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,188 @@
1
+ Metadata-Version: 2.5
2
+ Name: catagg
3
+ Version: 0.1.0.dev0
4
+ Summary: Catastrophe loss aggregation, from event loss tables to net exceedance probability curves.
5
+ Project-URL: Homepage, https://github.com/John-Amal/catagg
6
+ Project-URL: Documentation, https://john-amal.github.io/catagg/
7
+ Project-URL: Source, https://github.com/John-Amal/catagg
8
+ Project-URL: Changelog, https://github.com/John-Amal/catagg/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/John-Amal/catagg/issues
10
+ Author: Amal John
11
+ License-Expression: MIT
12
+ License-File: LICENSE
13
+ Keywords: actuarial,catastrophe-modelling,exceedance-probability,monte-carlo,reinsurance
14
+ Classifier: Development Status :: 3 - Alpha
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Programming Language :: Python :: 3.11
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
20
+ Classifier: Typing :: Typed
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: numpy>=1.26
23
+ Requires-Dist: pandas>=2.1
24
+ Requires-Dist: pyarrow>=15.0
25
+ Requires-Dist: scipy>=1.11
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.11; extra == 'dev'
28
+ Requires-Dist: pytest>=8.0; extra == 'dev'
29
+ Requires-Dist: ruff>=0.6; extra == 'dev'
30
+ Provides-Extra: docs
31
+ Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
32
+ Requires-Dist: mkdocs<2,>=1.6; extra == 'docs'
33
+ Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
34
+ Description-Content-Type: text/markdown
35
+
36
+ # catagg
37
+
38
+ **Catastrophe loss aggregation, from event loss tables to net exceedance probability curves.**
39
+
40
+ [![CI](https://github.com/John-Amal/catagg/actions/workflows/ci.yml/badge.svg)](https://github.com/John-Amal/catagg/actions)
41
+ [![PyPI](https://img.shields.io/pypi/v/catagg.svg)](https://pypi.org/project/catagg/)
42
+ [![Docs](https://img.shields.io/badge/docs-catagg-blue)](https://john-amal.github.io/catagg/)
43
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
44
+
45
+ ---
46
+
47
+ ## The question this answers
48
+
49
+ A reinsurer holds a portfolio exposed to several perils. The catastrophe models produce an **event loss table** — for each modelled event, an annual rate and a loss distribution. Two questions follow immediately, and every pricing, capital and reinsurance-purchase decision depends on them:
50
+
51
+ **How much loss should we expect in a bad year, and how much of it do we actually keep after our reinsurance programme responds?**
52
+
53
+ `catagg` answers both. It simulates the year loss table, builds the gross exceedance probability curves, applies the reinsurance structures in the correct order, and returns gross, ceded and net views — with the validation checks that show the answer can be trusted.
54
+
55
+ ![Gross versus net OEP curve for the example Swiss multi-peril portfolio](docs/img/gross_vs_net_oep.png)
56
+
57
+ *Example portfolio: three perils, 20% quota share, a 50m xs 50m per-occurrence layer with two reinstatements, and a 100m aggregate stop-loss. The programme removes roughly two thirds of the 200-year occurrence loss — and the reinstatement cap is visible as the point where the net curve stops flattening.*
58
+
59
+ ---
60
+
61
+ ## Install
62
+
63
+ ```bash
64
+ pip install catagg
65
+ ```
66
+
67
+ Python 3.11+. Runtime dependencies are numpy, pandas, pyarrow and scipy — nothing else.
68
+
69
+ ---
70
+
71
+ ## Quickstart
72
+
73
+ ```python
74
+ import catagg as ca
75
+
76
+ # 1. Load an event loss table
77
+ elt = ca.read_elt("portfolio.parquet")
78
+
79
+ # 2. Simulate 100,000 years
80
+ yelt = ca.simulate(
81
+ elt,
82
+ n_years=100_000,
83
+ frequency="negative_binomial", # or "poisson"
84
+ secondary_uncertainty=True,
85
+ seed=42,
86
+ )
87
+
88
+ # 3. Gross view
89
+ gross = ca.ep_curve(yelt)
90
+ print(gross.at_return_period([10, 50, 100, 200, 250]))
91
+
92
+ # 4. Apply a reinsurance programme
93
+ programme = ca.Programme([
94
+ ca.QuotaShare(cession=0.20),
95
+ ca.ExcessOfLoss(attachment=50e6, limit=50e6, reinstatements=2, basis="occurrence"),
96
+ ca.AggregateStopLoss(attachment=100e6, limit=100e6),
97
+ ])
98
+
99
+ result = programme.apply(yelt)
100
+ net = ca.ep_curve(result.net)
101
+
102
+ # 5. Compare
103
+ ca.plot.ep_comparison({"Gross": gross, "Net": net}, kind="OEP")
104
+ ```
105
+
106
+ Everything runs from a clean clone — the example portfolio is generated synthetically by a documented generator, so there are no data dependencies and no licensing questions.
107
+
108
+ ---
109
+
110
+ ## Concepts
111
+
112
+ If you already work in cat modelling, skip this. If you're reading the code to learn, this is the map.
113
+
114
+ | Object | What it is |
115
+ |---|---|
116
+ | **ELT** | Event loss table. One row per modelled event: annual rate λ, mean loss, standard deviation, exposed value. |
117
+ | **YELT** | Year event loss table. One row per event *occurrence* per simulated year. The engine's central output. |
118
+ | **YLT** | Year loss table. One row per simulated year: aggregate loss and maximum occurrence loss. |
119
+ | **OEP** | Occurrence exceedance probability. The distribution of the *largest single event* in a year. Drives per-occurrence cover. |
120
+ | **AEP** | Aggregate exceedance probability. The distribution of *total annual* loss. Drives aggregate cover and capital. |
121
+ | **AAL** | Average annual loss. The mean of the AEP distribution, and the cleanest check on the whole pipeline. |
122
+ | **TVaR** | Tail value at risk. The mean loss given exceedance of a return period. What capital regimes actually ask for. |
123
+
124
+ ---
125
+
126
+ ## What's in the engine
127
+
128
+ **Frequency.** Poisson, or negative binomial where event clustering matters — European windstorm and hurricane seasons are both over-dispersed, and a Poisson assumption understates the aggregate tail.
129
+
130
+ **Secondary uncertainty.** Loss given occurrence is sampled from a beta distribution on `[0, exposed_value]`, fitted by moments to the ELT's mean and standard deviation, with probability mass at zero and at full exposure. Degenerate cases — zero variance, or a variance the beta support cannot accommodate — are handled explicitly rather than silently clipped.
131
+
132
+ **Dependence.** Perils are correlated through a Gaussian or Student-t copula applied to annual losses. Independence is the default; the example shows what assuming it costs you in the tail.
133
+
134
+ **Financial structures.** Applied in programme order:
135
+
136
+ - Quota share, with optional ceding commission
137
+ - Per-occurrence excess of loss: attachment, limit, *n* reinstatements at stated percentages, pro-rata to amount, with the aggregate cap of `limit × (1 + n)` enforced
138
+ - Aggregate excess of loss and stop-loss
139
+ - Franchise deductible
140
+
141
+ **Metrics.** OEP, AEP, AAL, TVaR at any return period, full return-period tables, and Monte Carlo standard errors on every tail statistic.
142
+
143
+ ---
144
+
145
+ ## Validation
146
+
147
+ Numerical libraries in this domain live or die on whether you can show they're right. `catagg` ships a validation suite that runs in CI and as a readable notebook:
148
+
149
+ - Simulated AAL converges to the analytic `Σ λᵢ · μᵢ`
150
+ - Sampled frequency moments match the specified Poisson or negative binomial
151
+ - Sampled severity recovers the input mean and standard deviation
152
+ - Ceded loss never exceeds the aggregate reinstatement cap
153
+ - Net + ceded = gross, exactly, in every simulated year
154
+ - Tail-metric standard error as a function of simulation count
155
+
156
+ That last one matters more than it looks. A 200-year return period estimated from 10,000 simulation years carries a standard error large enough to change decisions, and most workflows never report it. `catagg` reports it by default.
157
+
158
+ See [docs/validation](https://john-amal.github.io/catagg/validation/).
159
+
160
+ ---
161
+
162
+ ## Scope
163
+
164
+ **In scope:** everything above.
165
+
166
+ **Deliberately out of scope for v0.1:** hazard modelling, vulnerability curves, location-level financial terms, exposure data management, a graphical interface, cloud orchestration.
167
+
168
+ `catagg` starts where the cat model stops. If you need the full modelling stack, use [Oasis LMF](https://oasislmf.org/). If you need to understand, audit or prototype the aggregation and financial layer, use this.
169
+
170
+ ---
171
+
172
+ ## Roadmap
173
+
174
+ - **v0.2** — location and policy-level terms, surplus share, reinstatement premium calculation
175
+ - **v0.3** — OED-compatible ELT ingest, `oasislmf` interoperability
176
+ - **v0.4** — event-level correlation across sub-perils, seasonality in event timing
177
+
178
+ Issues and pull requests are welcome, particularly from anyone who has implemented reinstatement logic in production and disagrees with this one.
179
+
180
+ ---
181
+
182
+ ## Citing
183
+
184
+ If you use `catagg` in published work, cite the Zenodo record: *(DOI added on first release.)*
185
+
186
+ ## Licence
187
+
188
+ MIT.
@@ -0,0 +1,153 @@
1
+ # catagg
2
+
3
+ **Catastrophe loss aggregation, from event loss tables to net exceedance probability curves.**
4
+
5
+ [![CI](https://github.com/John-Amal/catagg/actions/workflows/ci.yml/badge.svg)](https://github.com/John-Amal/catagg/actions)
6
+ [![PyPI](https://img.shields.io/pypi/v/catagg.svg)](https://pypi.org/project/catagg/)
7
+ [![Docs](https://img.shields.io/badge/docs-catagg-blue)](https://john-amal.github.io/catagg/)
8
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
9
+
10
+ ---
11
+
12
+ ## The question this answers
13
+
14
+ A reinsurer holds a portfolio exposed to several perils. The catastrophe models produce an **event loss table** — for each modelled event, an annual rate and a loss distribution. Two questions follow immediately, and every pricing, capital and reinsurance-purchase decision depends on them:
15
+
16
+ **How much loss should we expect in a bad year, and how much of it do we actually keep after our reinsurance programme responds?**
17
+
18
+ `catagg` answers both. It simulates the year loss table, builds the gross exceedance probability curves, applies the reinsurance structures in the correct order, and returns gross, ceded and net views — with the validation checks that show the answer can be trusted.
19
+
20
+ ![Gross versus net OEP curve for the example Swiss multi-peril portfolio](docs/img/gross_vs_net_oep.png)
21
+
22
+ *Example portfolio: three perils, 20% quota share, a 50m xs 50m per-occurrence layer with two reinstatements, and a 100m aggregate stop-loss. The programme removes roughly two thirds of the 200-year occurrence loss — and the reinstatement cap is visible as the point where the net curve stops flattening.*
23
+
24
+ ---
25
+
26
+ ## Install
27
+
28
+ ```bash
29
+ pip install catagg
30
+ ```
31
+
32
+ Python 3.11+. Runtime dependencies are numpy, pandas, pyarrow and scipy — nothing else.
33
+
34
+ ---
35
+
36
+ ## Quickstart
37
+
38
+ ```python
39
+ import catagg as ca
40
+
41
+ # 1. Load an event loss table
42
+ elt = ca.read_elt("portfolio.parquet")
43
+
44
+ # 2. Simulate 100,000 years
45
+ yelt = ca.simulate(
46
+ elt,
47
+ n_years=100_000,
48
+ frequency="negative_binomial", # or "poisson"
49
+ secondary_uncertainty=True,
50
+ seed=42,
51
+ )
52
+
53
+ # 3. Gross view
54
+ gross = ca.ep_curve(yelt)
55
+ print(gross.at_return_period([10, 50, 100, 200, 250]))
56
+
57
+ # 4. Apply a reinsurance programme
58
+ programme = ca.Programme([
59
+ ca.QuotaShare(cession=0.20),
60
+ ca.ExcessOfLoss(attachment=50e6, limit=50e6, reinstatements=2, basis="occurrence"),
61
+ ca.AggregateStopLoss(attachment=100e6, limit=100e6),
62
+ ])
63
+
64
+ result = programme.apply(yelt)
65
+ net = ca.ep_curve(result.net)
66
+
67
+ # 5. Compare
68
+ ca.plot.ep_comparison({"Gross": gross, "Net": net}, kind="OEP")
69
+ ```
70
+
71
+ Everything runs from a clean clone — the example portfolio is generated synthetically by a documented generator, so there are no data dependencies and no licensing questions.
72
+
73
+ ---
74
+
75
+ ## Concepts
76
+
77
+ If you already work in cat modelling, skip this. If you're reading the code to learn, this is the map.
78
+
79
+ | Object | What it is |
80
+ |---|---|
81
+ | **ELT** | Event loss table. One row per modelled event: annual rate λ, mean loss, standard deviation, exposed value. |
82
+ | **YELT** | Year event loss table. One row per event *occurrence* per simulated year. The engine's central output. |
83
+ | **YLT** | Year loss table. One row per simulated year: aggregate loss and maximum occurrence loss. |
84
+ | **OEP** | Occurrence exceedance probability. The distribution of the *largest single event* in a year. Drives per-occurrence cover. |
85
+ | **AEP** | Aggregate exceedance probability. The distribution of *total annual* loss. Drives aggregate cover and capital. |
86
+ | **AAL** | Average annual loss. The mean of the AEP distribution, and the cleanest check on the whole pipeline. |
87
+ | **TVaR** | Tail value at risk. The mean loss given exceedance of a return period. What capital regimes actually ask for. |
88
+
89
+ ---
90
+
91
+ ## What's in the engine
92
+
93
+ **Frequency.** Poisson, or negative binomial where event clustering matters — European windstorm and hurricane seasons are both over-dispersed, and a Poisson assumption understates the aggregate tail.
94
+
95
+ **Secondary uncertainty.** Loss given occurrence is sampled from a beta distribution on `[0, exposed_value]`, fitted by moments to the ELT's mean and standard deviation, with probability mass at zero and at full exposure. Degenerate cases — zero variance, or a variance the beta support cannot accommodate — are handled explicitly rather than silently clipped.
96
+
97
+ **Dependence.** Perils are correlated through a Gaussian or Student-t copula applied to annual losses. Independence is the default; the example shows what assuming it costs you in the tail.
98
+
99
+ **Financial structures.** Applied in programme order:
100
+
101
+ - Quota share, with optional ceding commission
102
+ - Per-occurrence excess of loss: attachment, limit, *n* reinstatements at stated percentages, pro-rata to amount, with the aggregate cap of `limit × (1 + n)` enforced
103
+ - Aggregate excess of loss and stop-loss
104
+ - Franchise deductible
105
+
106
+ **Metrics.** OEP, AEP, AAL, TVaR at any return period, full return-period tables, and Monte Carlo standard errors on every tail statistic.
107
+
108
+ ---
109
+
110
+ ## Validation
111
+
112
+ Numerical libraries in this domain live or die on whether you can show they're right. `catagg` ships a validation suite that runs in CI and as a readable notebook:
113
+
114
+ - Simulated AAL converges to the analytic `Σ λᵢ · μᵢ`
115
+ - Sampled frequency moments match the specified Poisson or negative binomial
116
+ - Sampled severity recovers the input mean and standard deviation
117
+ - Ceded loss never exceeds the aggregate reinstatement cap
118
+ - Net + ceded = gross, exactly, in every simulated year
119
+ - Tail-metric standard error as a function of simulation count
120
+
121
+ That last one matters more than it looks. A 200-year return period estimated from 10,000 simulation years carries a standard error large enough to change decisions, and most workflows never report it. `catagg` reports it by default.
122
+
123
+ See [docs/validation](https://john-amal.github.io/catagg/validation/).
124
+
125
+ ---
126
+
127
+ ## Scope
128
+
129
+ **In scope:** everything above.
130
+
131
+ **Deliberately out of scope for v0.1:** hazard modelling, vulnerability curves, location-level financial terms, exposure data management, a graphical interface, cloud orchestration.
132
+
133
+ `catagg` starts where the cat model stops. If you need the full modelling stack, use [Oasis LMF](https://oasislmf.org/). If you need to understand, audit or prototype the aggregation and financial layer, use this.
134
+
135
+ ---
136
+
137
+ ## Roadmap
138
+
139
+ - **v0.2** — location and policy-level terms, surplus share, reinstatement premium calculation
140
+ - **v0.3** — OED-compatible ELT ingest, `oasislmf` interoperability
141
+ - **v0.4** — event-level correlation across sub-perils, seasonality in event timing
142
+
143
+ Issues and pull requests are welcome, particularly from anyone who has implemented reinstatement logic in production and disagrees with this one.
144
+
145
+ ---
146
+
147
+ ## Citing
148
+
149
+ If you use `catagg` in published work, cite the Zenodo record: *(DOI added on first release.)*
150
+
151
+ ## Licence
152
+
153
+ MIT.
@@ -0,0 +1,3 @@
1
+ # API reference
2
+
3
+ ::: catagg
@@ -0,0 +1 @@
1
+ --8<-- "CHANGELOG.md"
File without changes
@@ -0,0 +1,23 @@
1
+ # catagg
2
+
3
+ Catastrophe loss aggregation, from event loss tables to net exceedance
4
+ probability curves.
5
+
6
+ !!! warning "Pre-release"
7
+
8
+ v0.1.0 is under development. The public API described in the README is the
9
+ specification being built against, not yet the shipped surface.
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pip install catagg
15
+ ```
16
+
17
+ Python 3.11+. Runtime dependencies are numpy, pandas, pyarrow and scipy.
18
+
19
+ ## Where to start
20
+
21
+ - **[Validation](validation.md)** — the checks that show the numbers can be trusted.
22
+ - **[API reference](api.md)** — the public surface.
23
+ - **[Changelog](changelog.md)** — what changed, and when.
@@ -0,0 +1,70 @@
1
+ # Releasing
2
+
3
+ `catagg` publishes to PyPI from GitHub Actions using
4
+ [Trusted Publishing](https://docs.pypi.org/trusted-publishers/). No API token
5
+ is created, stored or rotated.
6
+
7
+ ## One-off setup
8
+
9
+ Done once, before the project exists on PyPI.
10
+
11
+ ### 1. PyPI account
12
+
13
+ Create an account on [pypi.org](https://pypi.org) and enable two-factor
14
+ authentication. Uploads are refused without it.
15
+
16
+ ### 2. Register a pending publisher
17
+
18
+ PyPI lets you configure a publisher for a project that does not exist yet; the
19
+ first successful workflow run creates the project and claims the name.
20
+
21
+ Go to **Account settings → Publishing → Add a new pending publisher** and enter:
22
+
23
+ | Field | Value |
24
+ |---|---|
25
+ | PyPI project name | `catagg` |
26
+ | Owner | `John-Amal` |
27
+ | Repository name | `catagg` |
28
+ | Workflow name | `release.yml` |
29
+ | Environment name | `pypi` |
30
+
31
+ These must match exactly. A mismatch in any field makes the OIDC token invalid
32
+ and the upload fails with a permissions error rather than a helpful one.
33
+
34
+ ### 3. Create the GitHub environment
35
+
36
+ In the repository: **Settings → Environments → New environment**, named `pypi`.
37
+
38
+ Optionally add yourself as a required reviewer. That turns every publish into a
39
+ manual approval — worth it, given that an upload can never be undone.
40
+
41
+ ## Claiming the name
42
+
43
+ Run the workflow manually once, from **Actions → Release → Run workflow** on
44
+ `main`, while the version in `src/catagg/__init__.py` is still `0.1.0.dev0`.
45
+
46
+ This uploads a pre-release, which:
47
+
48
+ - claims the name `catagg` permanently;
49
+ - leaves `0.1.0` free for the real release;
50
+ - is invisible to `pip install catagg`, since pip ignores pre-releases unless
51
+ `--pre` is passed.
52
+
53
+ Confirm at <https://pypi.org/project/catagg/>.
54
+
55
+ ## Normal release
56
+
57
+ 1. Set the version in `src/catagg/__init__.py`. It is the single source; the
58
+ build reads it, and a test asserts it matches the distribution metadata.
59
+ 2. Move the `[Unreleased]` entries in `CHANGELOG.md` under the new version
60
+ heading with its date.
61
+ 3. Merge to `main` and wait for CI to pass.
62
+ 4. Tag and push: `git tag v0.1.0 && git push origin v0.1.0`.
63
+ 5. Publish the GitHub Release for that tag. The workflow refuses to run if the
64
+ tag and the package version disagree.
65
+ 6. Mint the Zenodo DOI and add it to the README's citation section.
66
+
67
+ ## Irreversibility
68
+
69
+ A version, once uploaded, can never be uploaded again — deleting a release does
70
+ not free its filename. Check the version number before publishing, not after.
@@ -0,0 +1,17 @@
1
+ # Validation
2
+
3
+ !!! note "Placeholder"
4
+
5
+ This page is a stub. It is filled by the validation notebook built at the
6
+ end of the v0.1.0 milestone; no results are published here yet.
7
+
8
+ The suite this page will report on, each check running in CI:
9
+
10
+ | Check | What it proves |
11
+ |---|---|
12
+ | Simulated AAL against analytic `Σ λᵢ · μᵢ` | The frequency–severity loop is unbiased |
13
+ | Sampled frequency moments against Poisson / negative binomial | The sampler matches the specified distribution |
14
+ | Sampled severity moments against the ELT input | Method-of-moments beta fitting recovers its own inputs |
15
+ | Ceded loss against the aggregate reinstatement cap | Reinstatement accounting never over-recovers |
16
+ | Net + ceded = gross, every simulated year | The financial module conserves loss exactly |
17
+ | Tail-metric standard error against simulation count | How noisy a given return period estimate actually is |
@@ -0,0 +1,135 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.27"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "catagg"
7
+ dynamic = ["version"]
8
+ description = "Catastrophe loss aggregation, from event loss tables to net exceedance probability curves."
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ license-files = ["LICENSE"]
13
+ authors = [{ name = "Amal John" }]
14
+ keywords = [
15
+ "catastrophe-modelling",
16
+ "reinsurance",
17
+ "exceedance-probability",
18
+ "actuarial",
19
+ "monte-carlo",
20
+ ]
21
+ classifiers = [
22
+ "Development Status :: 3 - Alpha",
23
+ "Intended Audience :: Science/Research",
24
+ "Programming Language :: Python :: 3.11",
25
+ "Programming Language :: Python :: 3.12",
26
+ "Programming Language :: Python :: 3.13",
27
+ "Typing :: Typed",
28
+ "Topic :: Scientific/Engineering :: Mathematics",
29
+ ]
30
+ dependencies = [
31
+ "numpy>=1.26",
32
+ "pandas>=2.1",
33
+ "pyarrow>=15.0",
34
+ "scipy>=1.11",
35
+ ]
36
+
37
+ [project.optional-dependencies]
38
+ dev = [
39
+ "mypy>=1.11",
40
+ "pytest>=8.0",
41
+ "ruff>=0.6",
42
+ ]
43
+ docs = [
44
+ "mkdocs>=1.6,<2",
45
+ "mkdocs-material>=9.5",
46
+ "mkdocstrings[python]>=0.26",
47
+ ]
48
+
49
+ [project.urls]
50
+ Homepage = "https://github.com/John-Amal/catagg"
51
+ Documentation = "https://john-amal.github.io/catagg/"
52
+ Source = "https://github.com/John-Amal/catagg"
53
+ Changelog = "https://github.com/John-Amal/catagg/blob/main/CHANGELOG.md"
54
+ Issues = "https://github.com/John-Amal/catagg/issues"
55
+
56
+ [tool.hatch.version]
57
+ path = "src/catagg/__init__.py"
58
+
59
+ [tool.hatch.build.targets.wheel]
60
+ packages = ["src/catagg"]
61
+
62
+ [tool.hatch.build.targets.sdist]
63
+ include = ["src", "tests", "docs", "CHANGELOG.md", "LICENSE", "README.md"]
64
+
65
+ # ---------------------------------------------------------------------------
66
+ # Lint / format
67
+ # ---------------------------------------------------------------------------
68
+ [tool.ruff]
69
+ line-length = 100
70
+ target-version = "py311"
71
+ src = ["src", "tests"]
72
+ # Ruff reformats Python blocks inside Markdown. The README is the project
73
+ # specification and is author-owned prose; a formatter should not rewrite it.
74
+ extend-exclude = ["*.md"]
75
+
76
+ [tool.ruff.lint]
77
+ select = [
78
+ "A", # builtin shadowing
79
+ "B", # bugbear
80
+ "C4", # comprehensions
81
+ "D", # docstrings
82
+ "E", # pycodestyle errors
83
+ "F", # pyflakes
84
+ "I", # import order
85
+ "N", # naming
86
+ "NPY", # numpy-specific
87
+ "PD", # pandas-vet
88
+ "PT", # pytest style
89
+ "RUF",
90
+ "SIM",
91
+ "UP", # pyupgrade
92
+ "W",
93
+ ]
94
+ ignore = [
95
+ "D105", # docstring in magic method
96
+ "D107", # docstring in __init__
97
+ ]
98
+
99
+ [tool.ruff.lint.pydocstyle]
100
+ convention = "numpy"
101
+
102
+ [tool.ruff.lint.per-file-ignores]
103
+ "tests/*" = ["D100", "D101", "D102", "D103", "D104"]
104
+ # `catagg.io` is the industry-legible name for the ingest layer and is only ever
105
+ # reached as an absolute import, so shadowing the stdlib module name is safe.
106
+ "src/catagg/io/*" = ["A005"]
107
+ "docs/*" = ["D"]
108
+
109
+ [tool.ruff.format]
110
+ docstring-code-format = true
111
+
112
+ # ---------------------------------------------------------------------------
113
+ # Types
114
+ # ---------------------------------------------------------------------------
115
+ [tool.mypy]
116
+ python_version = "3.11"
117
+ files = ["src", "tests"]
118
+ strict = true
119
+ warn_unreachable = true
120
+ pretty = true
121
+
122
+ # scipy and pyarrow ship incomplete type information; numpy and pandas are
123
+ # checked normally. Narrow these as the stubs improve rather than widening them.
124
+ [[tool.mypy.overrides]]
125
+ module = ["scipy.*", "pyarrow.*"]
126
+ ignore_missing_imports = true
127
+
128
+ # ---------------------------------------------------------------------------
129
+ # Tests
130
+ # ---------------------------------------------------------------------------
131
+ [tool.pytest.ini_options]
132
+ minversion = "8.0"
133
+ testpaths = ["tests"]
134
+ addopts = "-ra --strict-markers --strict-config"
135
+ xfail_strict = true
@@ -0,0 +1,5 @@
1
+ """Catastrophe loss aggregation, from event loss tables to net exceedance probability curves."""
2
+
3
+ __version__ = "0.1.0.dev0"
4
+
5
+ __all__ = ["__version__"]
@@ -0,0 +1 @@
1
+ """Cross-peril dependence: Gaussian and Student-t copulas over annual losses."""
@@ -0,0 +1 @@
1
+ """Reinsurance structures: quota share, excess of loss with reinstatements, stop-loss."""
@@ -0,0 +1 @@
1
+ """Event-count samplers: Poisson and negative binomial."""
@@ -0,0 +1 @@
1
+ """ELT readers, schema validation, and the Parquet YELT writer."""
@@ -0,0 +1 @@
1
+ """EP curves, AAL, TVaR, return-period tables and Monte Carlo standard errors."""
File without changes
@@ -0,0 +1 @@
1
+ """Secondary uncertainty: beta-with-point-mass sampler and moment fitting."""
@@ -0,0 +1 @@
1
+ """The year loop: ELT to YELT to YLT."""
@@ -0,0 +1,34 @@
1
+ """Checks that the distribution is assembled correctly.
2
+
3
+ These are not smoke tests: each one fails on a specific, real packaging
4
+ mistake that is otherwise silent until a user hits it.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import re
10
+ from importlib.metadata import version as dist_version
11
+ from importlib.resources import files
12
+
13
+ import catagg
14
+
15
+ PEP440 = re.compile(r"^\d+\.\d+\.\d+(?:(?:a|b|rc)\d+)?(?:\.post\d+)?(?:\.dev\d+)?$")
16
+
17
+
18
+ def test_version_is_pep440() -> None:
19
+ assert PEP440.match(catagg.__version__), catagg.__version__
20
+
21
+
22
+ def test_module_version_matches_distribution_metadata() -> None:
23
+ """The single source of truth must survive the build.
24
+
25
+ Fails if ``__init__.__version__`` and the built metadata drift apart --
26
+ the usual cause of a release tagged with one number and published under
27
+ another.
28
+ """
29
+ assert catagg.__version__ == dist_version("catagg")
30
+
31
+
32
+ def test_py_typed_marker_is_installed() -> None:
33
+ """Without this file downstream type checkers silently ignore the package."""
34
+ assert files("catagg").joinpath("py.typed").is_file()