risksim 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.
- risksim-0.1.0/LICENSE +21 -0
- risksim-0.1.0/PKG-INFO +275 -0
- risksim-0.1.0/README.md +250 -0
- risksim-0.1.0/pyproject.toml +28 -0
- risksim-0.1.0/setup.cfg +4 -0
- risksim-0.1.0/src/risksim/__init__.py +13 -0
- risksim-0.1.0/src/risksim/contracts.py +127 -0
- risksim-0.1.0/src/risksim/metrics.py +86 -0
- risksim-0.1.0/src/risksim/portfolio.py +147 -0
- risksim-0.1.0/src/risksim/protocols.py +24 -0
- risksim-0.1.0/src/risksim/results.py +170 -0
- risksim-0.1.0/src/risksim.egg-info/PKG-INFO +275 -0
- risksim-0.1.0/src/risksim.egg-info/SOURCES.txt +20 -0
- risksim-0.1.0/src/risksim.egg-info/dependency_links.txt +1 -0
- risksim-0.1.0/src/risksim.egg-info/requires.txt +1 -0
- risksim-0.1.0/src/risksim.egg-info/top_level.txt +1 -0
- risksim-0.1.0/tests/test_contracts.py +113 -0
- risksim-0.1.0/tests/test_integration_lossmodels.py +0 -0
- risksim-0.1.0/tests/test_metrics.py +70 -0
- risksim-0.1.0/tests/test_portfolio.py +211 -0
- risksim-0.1.0/tests/test_protocols.py +31 -0
- risksim-0.1.0/tests/test_results.py +132 -0
risksim-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Michael Bryant
|
|
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.
|
risksim-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: risksim
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Portfolio-level loss simulation and aggregate treaty modeling in Python
|
|
5
|
+
Author: Michael Bryant
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/michaelabryant/risksim
|
|
8
|
+
Project-URL: Repository, https://github.com/michaelabryant/risksim
|
|
9
|
+
Project-URL: Issues, https://github.com/michaelabryant/risksim/issues
|
|
10
|
+
Keywords: actuarial,insurance,simulation,risk,reinsurance
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
19
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: numpy
|
|
24
|
+
Dynamic: license-file
|
|
25
|
+
|
|
26
|
+
# risksim
|
|
27
|
+
|
|
28
|
+
`risksim` is a Python package for portfolio-level loss simulation and aggregate treaty modeling.
|
|
29
|
+
|
|
30
|
+
It is designed to sit on top of stochastic loss models that expose a `.sample(size)` method. In particular, it pairs naturally with `lossmodels`, where `lossmodels` handles loss-model generation and `risksim` handles portfolio composition, aggregate contracts, and simulation summaries.
|
|
31
|
+
|
|
32
|
+
## Features
|
|
33
|
+
|
|
34
|
+
- combine multiple simulated loss components into a portfolio
|
|
35
|
+
- apply aggregate annual layers
|
|
36
|
+
- build simple multi-layer aggregate contract programs
|
|
37
|
+
- summarize gross, ceded, and retained loss
|
|
38
|
+
- compute simulation-based risk measures such as mean, VaR, and TVaR
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
### From PyPI
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
pip install risksim
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### From source
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
git clone https://github.com/michaelabryant/risksim.git
|
|
52
|
+
cd risksim
|
|
53
|
+
pip install -e .
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Optional companion package
|
|
57
|
+
|
|
58
|
+
`risksim` works with any model that implements:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
sample(size: int = 1) -> np.ndarray
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Many of the examples below use [`lossmodels`](https://github.com/michaelabryant/lossmodels), which provides aggregate actuarial loss models that integrate naturally with `risksim`.
|
|
65
|
+
|
|
66
|
+
If you want to run the `lossmodels` examples locally, install `lossmodels` as well.
|
|
67
|
+
|
|
68
|
+
## Quick start
|
|
69
|
+
|
|
70
|
+
Any object with a `.sample(size)` method can be used inside a `Portfolio`.
|
|
71
|
+
|
|
72
|
+
```python
|
|
73
|
+
import numpy as np
|
|
74
|
+
|
|
75
|
+
from risksim import AggregateLayer, Portfolio, PortfolioItem
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ConstantModel:
|
|
79
|
+
def __init__(self, value: float) -> None:
|
|
80
|
+
self.value = float(value)
|
|
81
|
+
|
|
82
|
+
def sample(self, size: int = 1) -> np.ndarray:
|
|
83
|
+
return np.full(size, self.value, dtype=float)
|
|
84
|
+
|
|
85
|
+
def mean(self) -> float:
|
|
86
|
+
return self.value
|
|
87
|
+
|
|
88
|
+
def variance(self) -> float:
|
|
89
|
+
return 0.0
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
medical = ConstantModel(200.0)
|
|
93
|
+
rx = ConstantModel(50.0)
|
|
94
|
+
|
|
95
|
+
portfolio = Portfolio(
|
|
96
|
+
[
|
|
97
|
+
PortfolioItem("medical", medical),
|
|
98
|
+
PortfolioItem("rx", rx),
|
|
99
|
+
]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
contract = AggregateLayer(
|
|
103
|
+
attachment=100.0,
|
|
104
|
+
limit=75.0,
|
|
105
|
+
share=1.0,
|
|
106
|
+
name="aggregate_xol",
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
result = portfolio.simulate(size=10_000, contract=contract)
|
|
110
|
+
|
|
111
|
+
print("net mean:", result.mean())
|
|
112
|
+
print("gross mean:", result.gross_mean())
|
|
113
|
+
print("ceded mean:", result.ceded_mean())
|
|
114
|
+
print("VaR 99%:", result.var(0.99))
|
|
115
|
+
print("TVaR 99%:", result.tvar(0.99))
|
|
116
|
+
print(result.summary())
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## Using `lossmodels`
|
|
120
|
+
|
|
121
|
+
A common workflow is to build aggregate loss models with `lossmodels` and then combine them with `risksim`.
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from lossmodels.aggregate import CollectiveRiskModel
|
|
125
|
+
from lossmodels.frequency import Poisson
|
|
126
|
+
from lossmodels.severity import Lognormal
|
|
127
|
+
|
|
128
|
+
from risksim import AggregateLayer, Portfolio, PortfolioItem
|
|
129
|
+
|
|
130
|
+
medical = CollectiveRiskModel(
|
|
131
|
+
Poisson(lam=2.0),
|
|
132
|
+
Lognormal(mu=2.0, sigma=0.3),
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
rx = CollectiveRiskModel(
|
|
136
|
+
Poisson(lam=1.0),
|
|
137
|
+
Lognormal(mu=1.5, sigma=0.2),
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
portfolio = Portfolio(
|
|
141
|
+
[
|
|
142
|
+
PortfolioItem("medical", medical),
|
|
143
|
+
PortfolioItem("rx", rx),
|
|
144
|
+
]
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
contract = AggregateLayer(
|
|
148
|
+
attachment=50.0,
|
|
149
|
+
limit=100.0,
|
|
150
|
+
name="agg_xol",
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
result = portfolio.simulate(size=50_000, contract=contract)
|
|
154
|
+
print(result.summary())
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Contract programs
|
|
158
|
+
|
|
159
|
+
`risksim` also supports simple multi-layer aggregate contract programs.
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
from lossmodels.aggregate import CollectiveRiskModel
|
|
163
|
+
from lossmodels.frequency import Poisson
|
|
164
|
+
from lossmodels.severity import Lognormal
|
|
165
|
+
|
|
166
|
+
from risksim import AggregateLayer, ContractProgram, Portfolio, PortfolioItem
|
|
167
|
+
|
|
168
|
+
line = CollectiveRiskModel(
|
|
169
|
+
Poisson(lam=4.0),
|
|
170
|
+
Lognormal(mu=3.5, sigma=0.7),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
portfolio = Portfolio([PortfolioItem("line", line)])
|
|
174
|
+
|
|
175
|
+
program = ContractProgram(
|
|
176
|
+
[
|
|
177
|
+
AggregateLayer(attachment=50.0, limit=50.0, name="layer_1"),
|
|
178
|
+
AggregateLayer(attachment=100.0, limit=100.0, name="layer_2"),
|
|
179
|
+
],
|
|
180
|
+
name="two_layer_tower",
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
result = portfolio.simulate(size=20_000, contract=program)
|
|
184
|
+
|
|
185
|
+
print("gross mean:", result.gross_mean())
|
|
186
|
+
print("ceded mean:", result.ceded_mean())
|
|
187
|
+
print("retained mean:", result.retained_mean())
|
|
188
|
+
print("layer means:", result.layer_means())
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Public API
|
|
192
|
+
|
|
193
|
+
```python
|
|
194
|
+
from risksim import AggregateLayer, ContractProgram, Portfolio, PortfolioItem, SimulationResult
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Core concepts
|
|
198
|
+
|
|
199
|
+
### `PortfolioItem`
|
|
200
|
+
Wraps one simulated component with a name and optional weight.
|
|
201
|
+
|
|
202
|
+
### `Portfolio`
|
|
203
|
+
Combines multiple simulated components into a total portfolio loss.
|
|
204
|
+
|
|
205
|
+
### `AggregateLayer`
|
|
206
|
+
Applies a single aggregate annual contract to simulated gross losses.
|
|
207
|
+
|
|
208
|
+
For annual gross loss `S`, ceded loss is:
|
|
209
|
+
|
|
210
|
+
```text
|
|
211
|
+
share * min(max(S - attachment, 0), limit)
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
with no cap if `limit=None`.
|
|
215
|
+
|
|
216
|
+
### `ContractProgram`
|
|
217
|
+
Combines multiple aggregate layers applied to the same gross loss.
|
|
218
|
+
|
|
219
|
+
### `SimulationResult`
|
|
220
|
+
Stores simulation outputs and provides convenience methods such as:
|
|
221
|
+
|
|
222
|
+
- `mean()`
|
|
223
|
+
- `variance()`
|
|
224
|
+
- `std()`
|
|
225
|
+
- `var(q)`
|
|
226
|
+
- `tvar(q)`
|
|
227
|
+
- `prob_exceeding(threshold)`
|
|
228
|
+
- `summary()`
|
|
229
|
+
|
|
230
|
+
If retained losses are present, the primary `losses` view is net/retained loss. Otherwise it is gross loss.
|
|
231
|
+
|
|
232
|
+
## Example scripts
|
|
233
|
+
|
|
234
|
+
The `examples/` directory contains runnable scripts:
|
|
235
|
+
|
|
236
|
+
- `examples/basic_portfolio.py`
|
|
237
|
+
- `examples/aggregate_layer.py`
|
|
238
|
+
- `examples/contract_program.py`
|
|
239
|
+
|
|
240
|
+
Run them with:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
python examples/basic_portfolio.py
|
|
244
|
+
python examples/aggregate_layer.py
|
|
245
|
+
python examples/contract_program.py
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## Project scope
|
|
249
|
+
|
|
250
|
+
The current version focuses on:
|
|
251
|
+
|
|
252
|
+
- portfolio-level simulation from component loss models
|
|
253
|
+
- aggregate annual treaty application
|
|
254
|
+
- simple non-overlapping multi-layer programs
|
|
255
|
+
- simulation-based portfolio summaries
|
|
256
|
+
|
|
257
|
+
It does not yet model:
|
|
258
|
+
|
|
259
|
+
- dependence between components
|
|
260
|
+
- occurrence-based contracts requiring claim-level paths
|
|
261
|
+
- reinstatements
|
|
262
|
+
- capital allocation
|
|
263
|
+
- premium or pricing workflows beyond simulated summaries
|
|
264
|
+
|
|
265
|
+
## Development
|
|
266
|
+
|
|
267
|
+
Run the test suite with:
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
python -m pytest -q
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
## License
|
|
274
|
+
|
|
275
|
+
MIT
|
risksim-0.1.0/README.md
ADDED
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
# risksim
|
|
2
|
+
|
|
3
|
+
`risksim` is a Python package for portfolio-level loss simulation and aggregate treaty modeling.
|
|
4
|
+
|
|
5
|
+
It is designed to sit on top of stochastic loss models that expose a `.sample(size)` method. In particular, it pairs naturally with `lossmodels`, where `lossmodels` handles loss-model generation and `risksim` handles portfolio composition, aggregate contracts, and simulation summaries.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- combine multiple simulated loss components into a portfolio
|
|
10
|
+
- apply aggregate annual layers
|
|
11
|
+
- build simple multi-layer aggregate contract programs
|
|
12
|
+
- summarize gross, ceded, and retained loss
|
|
13
|
+
- compute simulation-based risk measures such as mean, VaR, and TVaR
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
### From PyPI
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pip install risksim
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### From source
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
git clone https://github.com/michaelabryant/risksim.git
|
|
27
|
+
cd risksim
|
|
28
|
+
pip install -e .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Optional companion package
|
|
32
|
+
|
|
33
|
+
`risksim` works with any model that implements:
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
sample(size: int = 1) -> np.ndarray
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Many of the examples below use [`lossmodels`](https://github.com/michaelabryant/lossmodels), which provides aggregate actuarial loss models that integrate naturally with `risksim`.
|
|
40
|
+
|
|
41
|
+
If you want to run the `lossmodels` examples locally, install `lossmodels` as well.
|
|
42
|
+
|
|
43
|
+
## Quick start
|
|
44
|
+
|
|
45
|
+
Any object with a `.sample(size)` method can be used inside a `Portfolio`.
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
import numpy as np
|
|
49
|
+
|
|
50
|
+
from risksim import AggregateLayer, Portfolio, PortfolioItem
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ConstantModel:
|
|
54
|
+
def __init__(self, value: float) -> None:
|
|
55
|
+
self.value = float(value)
|
|
56
|
+
|
|
57
|
+
def sample(self, size: int = 1) -> np.ndarray:
|
|
58
|
+
return np.full(size, self.value, dtype=float)
|
|
59
|
+
|
|
60
|
+
def mean(self) -> float:
|
|
61
|
+
return self.value
|
|
62
|
+
|
|
63
|
+
def variance(self) -> float:
|
|
64
|
+
return 0.0
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
medical = ConstantModel(200.0)
|
|
68
|
+
rx = ConstantModel(50.0)
|
|
69
|
+
|
|
70
|
+
portfolio = Portfolio(
|
|
71
|
+
[
|
|
72
|
+
PortfolioItem("medical", medical),
|
|
73
|
+
PortfolioItem("rx", rx),
|
|
74
|
+
]
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
contract = AggregateLayer(
|
|
78
|
+
attachment=100.0,
|
|
79
|
+
limit=75.0,
|
|
80
|
+
share=1.0,
|
|
81
|
+
name="aggregate_xol",
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
result = portfolio.simulate(size=10_000, contract=contract)
|
|
85
|
+
|
|
86
|
+
print("net mean:", result.mean())
|
|
87
|
+
print("gross mean:", result.gross_mean())
|
|
88
|
+
print("ceded mean:", result.ceded_mean())
|
|
89
|
+
print("VaR 99%:", result.var(0.99))
|
|
90
|
+
print("TVaR 99%:", result.tvar(0.99))
|
|
91
|
+
print(result.summary())
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## Using `lossmodels`
|
|
95
|
+
|
|
96
|
+
A common workflow is to build aggregate loss models with `lossmodels` and then combine them with `risksim`.
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from lossmodels.aggregate import CollectiveRiskModel
|
|
100
|
+
from lossmodels.frequency import Poisson
|
|
101
|
+
from lossmodels.severity import Lognormal
|
|
102
|
+
|
|
103
|
+
from risksim import AggregateLayer, Portfolio, PortfolioItem
|
|
104
|
+
|
|
105
|
+
medical = CollectiveRiskModel(
|
|
106
|
+
Poisson(lam=2.0),
|
|
107
|
+
Lognormal(mu=2.0, sigma=0.3),
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
rx = CollectiveRiskModel(
|
|
111
|
+
Poisson(lam=1.0),
|
|
112
|
+
Lognormal(mu=1.5, sigma=0.2),
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
portfolio = Portfolio(
|
|
116
|
+
[
|
|
117
|
+
PortfolioItem("medical", medical),
|
|
118
|
+
PortfolioItem("rx", rx),
|
|
119
|
+
]
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
contract = AggregateLayer(
|
|
123
|
+
attachment=50.0,
|
|
124
|
+
limit=100.0,
|
|
125
|
+
name="agg_xol",
|
|
126
|
+
)
|
|
127
|
+
|
|
128
|
+
result = portfolio.simulate(size=50_000, contract=contract)
|
|
129
|
+
print(result.summary())
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Contract programs
|
|
133
|
+
|
|
134
|
+
`risksim` also supports simple multi-layer aggregate contract programs.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
from lossmodels.aggregate import CollectiveRiskModel
|
|
138
|
+
from lossmodels.frequency import Poisson
|
|
139
|
+
from lossmodels.severity import Lognormal
|
|
140
|
+
|
|
141
|
+
from risksim import AggregateLayer, ContractProgram, Portfolio, PortfolioItem
|
|
142
|
+
|
|
143
|
+
line = CollectiveRiskModel(
|
|
144
|
+
Poisson(lam=4.0),
|
|
145
|
+
Lognormal(mu=3.5, sigma=0.7),
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
portfolio = Portfolio([PortfolioItem("line", line)])
|
|
149
|
+
|
|
150
|
+
program = ContractProgram(
|
|
151
|
+
[
|
|
152
|
+
AggregateLayer(attachment=50.0, limit=50.0, name="layer_1"),
|
|
153
|
+
AggregateLayer(attachment=100.0, limit=100.0, name="layer_2"),
|
|
154
|
+
],
|
|
155
|
+
name="two_layer_tower",
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
result = portfolio.simulate(size=20_000, contract=program)
|
|
159
|
+
|
|
160
|
+
print("gross mean:", result.gross_mean())
|
|
161
|
+
print("ceded mean:", result.ceded_mean())
|
|
162
|
+
print("retained mean:", result.retained_mean())
|
|
163
|
+
print("layer means:", result.layer_means())
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Public API
|
|
167
|
+
|
|
168
|
+
```python
|
|
169
|
+
from risksim import AggregateLayer, ContractProgram, Portfolio, PortfolioItem, SimulationResult
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Core concepts
|
|
173
|
+
|
|
174
|
+
### `PortfolioItem`
|
|
175
|
+
Wraps one simulated component with a name and optional weight.
|
|
176
|
+
|
|
177
|
+
### `Portfolio`
|
|
178
|
+
Combines multiple simulated components into a total portfolio loss.
|
|
179
|
+
|
|
180
|
+
### `AggregateLayer`
|
|
181
|
+
Applies a single aggregate annual contract to simulated gross losses.
|
|
182
|
+
|
|
183
|
+
For annual gross loss `S`, ceded loss is:
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
share * min(max(S - attachment, 0), limit)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
with no cap if `limit=None`.
|
|
190
|
+
|
|
191
|
+
### `ContractProgram`
|
|
192
|
+
Combines multiple aggregate layers applied to the same gross loss.
|
|
193
|
+
|
|
194
|
+
### `SimulationResult`
|
|
195
|
+
Stores simulation outputs and provides convenience methods such as:
|
|
196
|
+
|
|
197
|
+
- `mean()`
|
|
198
|
+
- `variance()`
|
|
199
|
+
- `std()`
|
|
200
|
+
- `var(q)`
|
|
201
|
+
- `tvar(q)`
|
|
202
|
+
- `prob_exceeding(threshold)`
|
|
203
|
+
- `summary()`
|
|
204
|
+
|
|
205
|
+
If retained losses are present, the primary `losses` view is net/retained loss. Otherwise it is gross loss.
|
|
206
|
+
|
|
207
|
+
## Example scripts
|
|
208
|
+
|
|
209
|
+
The `examples/` directory contains runnable scripts:
|
|
210
|
+
|
|
211
|
+
- `examples/basic_portfolio.py`
|
|
212
|
+
- `examples/aggregate_layer.py`
|
|
213
|
+
- `examples/contract_program.py`
|
|
214
|
+
|
|
215
|
+
Run them with:
|
|
216
|
+
|
|
217
|
+
```bash
|
|
218
|
+
python examples/basic_portfolio.py
|
|
219
|
+
python examples/aggregate_layer.py
|
|
220
|
+
python examples/contract_program.py
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Project scope
|
|
224
|
+
|
|
225
|
+
The current version focuses on:
|
|
226
|
+
|
|
227
|
+
- portfolio-level simulation from component loss models
|
|
228
|
+
- aggregate annual treaty application
|
|
229
|
+
- simple non-overlapping multi-layer programs
|
|
230
|
+
- simulation-based portfolio summaries
|
|
231
|
+
|
|
232
|
+
It does not yet model:
|
|
233
|
+
|
|
234
|
+
- dependence between components
|
|
235
|
+
- occurrence-based contracts requiring claim-level paths
|
|
236
|
+
- reinstatements
|
|
237
|
+
- capital allocation
|
|
238
|
+
- premium or pricing workflows beyond simulated summaries
|
|
239
|
+
|
|
240
|
+
## Development
|
|
241
|
+
|
|
242
|
+
Run the test suite with:
|
|
243
|
+
|
|
244
|
+
```bash
|
|
245
|
+
python -m pytest -q
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
## License
|
|
249
|
+
|
|
250
|
+
MIT
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "risksim"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "Portfolio-level loss simulation and aggregate treaty modeling in Python"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
requires-python = ">=3.10"
|
|
7
|
+
dependencies = ["numpy"]
|
|
8
|
+
keywords = ["actuarial", "insurance", "simulation", "risk", "reinsurance"]
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Michael Bryant" }
|
|
11
|
+
]
|
|
12
|
+
license = { text = "MIT" }
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Development Status :: 3 - Alpha",
|
|
15
|
+
"Intended Audience :: Science/Research",
|
|
16
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.10",
|
|
20
|
+
"Topic :: Scientific/Engineering :: Mathematics",
|
|
21
|
+
"Topic :: Scientific/Engineering :: Information Analysis",
|
|
22
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
|
+
]
|
|
24
|
+
|
|
25
|
+
[project.urls]
|
|
26
|
+
Homepage = "https://github.com/michaelabryant/risksim"
|
|
27
|
+
Repository = "https://github.com/michaelabryant/risksim"
|
|
28
|
+
Issues = "https://github.com/michaelabryant/risksim/issues"
|
risksim-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .contracts import AggregateLayer, ContractProgram
|
|
2
|
+
from .portfolio import Portfolio, PortfolioItem
|
|
3
|
+
from .results import SimulationResult
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"AggregateLayer",
|
|
7
|
+
"ContractProgram",
|
|
8
|
+
"Portfolio",
|
|
9
|
+
"PortfolioItem",
|
|
10
|
+
"SimulationResult",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
__version__ = "0.1.0"
|