Skip to main content

risksim

CI PyPI

Portfolio-level loss simulation and aggregate reinsurance modeling.


Overview

risksim composes one or more stochastic loss models into a portfolio, simulates the portfolio's aggregate loss, applies aggregate reinsurance structures, and summarizes the gross, ceded, and retained results. It is intentionally model-agnostic: anything that exposes a .sample(size) method can be a portfolio component, so risksim handles composition, contracts, and simulation summaries while the loss generation lives wherever you like.

It pairs naturally with lossmodels — build a collective-risk model (or any severity/frequency-based model) there, drop it into a risksim portfolio here — but it has no hard dependency on it; its only runtime requirement is numpy.

Highlights

  • Portfolios — combine multiple simulated loss components, each with an optional weight, into a single portfolio.
  • Aggregate reinsurance — aggregate annual layers (attachment, limit, share) and multi-layer contract programs.
  • One simulation, every view — a single run yields gross, ceded, retained, per-component, and per-layer losses.
  • Risk measures — mean, variance / standard deviation, VaR, TVaR, exceedance probabilities, and a one-call summary, on any loss vector.
  • Model-agnostic — any object implementing .sample(size) works; components that also expose .mean() unlock closed-form portfolio moments.

Installation

pip install risksim

From source:

pip install -e .

Requires Python >=3.10; the only runtime dependency is numpy. The examples below use lossmodels for the component models — install it too if you want to run them as written.

Package structure

risksim/
├── portfolio.py    # Portfolio and PortfolioItem
├── contracts.py    # AggregateLayer, ContractProgram, apply_contract
├── results.py      # SimulationResult (gross / ceded / retained / component / layer views)
├── metrics.py      # mean, variance, std, var, tvar, prob_exceeding, summary
└── protocols.py    # SupportsSample, SupportsMoments

Quick start

from lossmodels import Poisson, Lognormal, CollectiveRiskModel
from risksim import Portfolio, PortfolioItem, AggregateLayer

# two lines of business, each a collective-risk model (any .sample object works)
line_a = CollectiveRiskModel(Poisson(3.0), Lognormal(8.0, 0.9))
line_b = CollectiveRiskModel(Poisson(1.5), Lognormal(9.0, 0.7))

portfolio = Portfolio([
    PortfolioItem("line_a", line_a),
    PortfolioItem("line_b", line_b, weight=1.25),   # optional exposure weight
])

# simulate the portfolio's aggregate annual loss, net of an aggregate layer
layer = AggregateLayer(attachment=250_000, limit=500_000, share=1.0)
result = portfolio.simulate(100_000, contract=layer)

print("gross mean   :", result.gross_mean)
print("ceded mean   :", result.ceded_mean)
print("retained mean:", result.retained_mean)
print("retained 99% VaR :", result.var(0.99))
print(result.summary())

Portfolios

A PortfolioItem wraps a sampleable loss model with a name and an optional weight (a multiplier applied to that component's sampled losses). A Portfolio is a sequence of items.

from risksim import Portfolio, PortfolioItem

port = Portfolio([PortfolioItem("a", model_a), PortfolioItem("b", model_b, weight=0.5)])

gross = port.sample(50_000)              # total portfolio loss per simulation
by_component = port.sample_components(50_000)  # per-component loss matrix
print(port.component_names())

When every component also exposes .mean() / .variance() (the SupportsMoments protocol — which lossmodels models satisfy), the portfolio reports closed-form moments without simulating:

port.mean()       # sum of component means (times weights)
port.std()
port.variance()

simulate(size, contract=None) runs the portfolio and returns a SimulationResult; sample(size) returns just the gross loss vector.

Aggregate reinsurance

An AggregateLayer is an aggregate annual structure defined by three numbers:

  • attachment — the aggregate retention; losses below it are not ceded.
  • limit — the width of the layer above the attachment (None = unlimited).
  • share — the ceded proportion of the layer (e.g. 0.8 for an 80% placement).

For a gross annual loss L, the ceded amount is share × min(max(L − attachment, 0), limit).

from risksim import AggregateLayer, ContractProgram, apply_contract

# a single layer: 80% of the 500k aggregate layer above a 250k retention
layer = AggregateLayer(attachment=250_000, limit=500_000, share=0.8)

# stack layers into a program (e.g. a working layer plus a higher layer)
program = ContractProgram([
    AggregateLayer(attachment=250_000, limit=500_000, share=1.0, name="working"),
    AggregateLayer(attachment=750_000, limit=1_000_000, share=0.5, name="higher"),
])

# apply either to a loss vector directly
ceded, retained = apply_contract(gross_losses, program)

apply_contract(losses, contract) returns (ceded, retained) arrays aligned with the input, where ceded + retained == losses. Passing a contract to Portfolio.simulate(..., contract=program) does the same inside the simulation and records the per-layer detail.

The simulation result

SimulationResult holds every view from a single run and computes risk measures on demand:

Attribute / method Meaning
gross_losses, ceded_losses, retained_losses aligned loss vectors
component_losses, component_names per-component loss matrix
layer_losses, layer_names per-layer ceded amounts (with a contract)
gross_mean, ceded_mean, retained_mean mean of each view
component_means, layer_means per-component / per-layer means
losses the retained vector (the portfolio's net position)
mean(), std(), variance() moments of the net loss
var(q), tvar(q) Value-at-Risk and Tail-VaR of the net loss
prob_exceeding(threshold) P(net loss > threshold)
summary() a dict of headline statistics
n_sims the number of simulations

Risk measures

The metrics module operates on any loss vector (a NumPy array or list), so you can use it independently of the portfolio machinery:

from risksim import metrics

metrics.mean(losses)
metrics.std(losses)            # metrics.variance(...) also available
metrics.var(losses, 0.99)      # 99% VaR
metrics.tvar(losses, 0.99)     # 99% TVaR
metrics.prob_exceeding(losses, 1_000_000)
metrics.summary(losses, quantiles=(0.95, 0.99, 0.995))

The SupportsSample protocol

Any object implementing sample(size: int) -> np.ndarray can be a portfolio component — a lossmodels collective-risk model or severity, a custom class, or a thin wrapper around a sampler. Components that additionally implement mean() / variance() (SupportsMoments) enable the closed-form Portfolio.mean() / .std() / .variance() shortcuts.

Testing

pytest -q

License

MIT License

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

risksim-0.5.0.tar.gz (30.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

risksim-0.5.0-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file risksim-0.5.0.tar.gz.

File metadata

  • Download URL: risksim-0.5.0.tar.gz
  • Upload date:
  • Size: 30.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for risksim-0.5.0.tar.gz
Algorithm Hash digest
SHA256 9b6af34fcffc974ef60b9f464de6c595bf86a0ca79863dd6a712c6b0f2c22127
MD5 c89fbb3aa814f5fc5aae58b3c486f45d
BLAKE2b-256 4554bcd84465714e801ba4383ab7bcc2d0a38b57a619956c853661ddd8c1b371

See more details on using hashes here.

Provenance

The following attestation bundles were made for risksim-0.5.0.tar.gz:

Publisher: release.yml on OpenActuarial/risksim

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file risksim-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: risksim-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 17.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for risksim-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 6b311b0f5f22e7ab9a66fab63d590796689813617236ba6682ec0fa2297b7edb
MD5 fc372a8a8efe839d773f0f246ca393af
BLAKE2b-256 cdae85dbf8ef1eb7debf4fe66ec7d506502911fc4f862b7437983dbca046964e

See more details on using hashes here.

Provenance

The following attestation bundles were made for risksim-0.5.0-py3-none-any.whl:

Publisher: release.yml on OpenActuarial/risksim

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page