# Dissecting a contract

> One CatXoL applied to one trial's losses term by term (filter, contract period, occurrence layer, aggregate limit), then participation at roll-up.

Source: https://reinsurance.dev/financial-modelling/dissection/

Financial modelling turns a cedent's **[subject loss](https://reinsurance.dev/glossary/#subject-loss)** into the reinsurer's **[gross loss](https://reinsurance.dev/glossary/#gross-loss)** by applying a contract's terms. The fastest way to understand the mechanism is to watch it happen: take one contract, one trial, and apply each term in turn.

:::note[Terminology: subject and gross]
This chapter adopts the reinsurer's perspective throughout, following the [loss perspectives](https://reinsurance.dev/foundations/products-and-contracts/#perspectives-on-loss) established in Foundations. What the cedent calls "ceded loss" is the reinsurer's "gross loss": the same dollar amount, seen from opposite sides of the contract. Since this site teaches reinsurance analytics from the reinsurer's point of view, we use **subject** for what goes into a contract and **gross** for what comes out.
:::

## Contracts define the financial terms

Financial modelling is the application of **financial terms**, and a contract is where those terms are written down. A reinsurance program is a stack of contracts, and each contract specifies how it reshapes the losses it sees: which perils it covers, when it attaches, how much it pays, and where it stops.

Each financial term is a **transformation** on trial loss data: it takes a [year event loss table (YELT)](https://reinsurance.dev/glossary/#yelt-year-event-loss-table), or the output of a preceding term, as input and produces a modified set of losses as output. Applying a contract means composing its terms into a pipeline that transforms the cedent's subject losses into the reinsurer's gross loss.

[Foundations](https://reinsurance.dev/foundations/products-and-contracts/) introduced the common contract types: **[quota share](https://reinsurance.dev/glossary/#quota-share)**, **[catastrophe excess of loss](https://reinsurance.dev/glossary/#catxol-catastrophe-excess-of-loss)** (CatXoL), and **[aggregate excess of loss](https://reinsurance.dev/glossary/#aggxol-aggregate-excess-of-loss)** (AggXoL, also called stop loss). Each comes in variants and carries optional features (reinstatements, event limits, franchise deductibles), each defined in the [glossary](https://reinsurance.dev/glossary/). But underneath the taxonomy, contracts are far less varied than their names suggest:

:::tip[Key Insight]
A specific contract is a **composition** of a small set of common financial terms. Quota share, CatXoL, and AggXoL differ not in the terms they are built from, but in how those terms are composed.
:::

The same few operations keep reappearing across contract types. [Contracts as compositions](https://reinsurance.dev/financial-modelling/composition/) tabulates which term each type uses, generated from the catalog itself. Caldera Re's Contract 1 composes those terms into a working CatXoL, dissected here one financial term at a time; the [term catalog](https://reinsurance.dev/financial-modelling/terms/) formalises each term.

## One trial at a time

One structural property governs how financial modelling is reasoned about and computed: **contract terms are applied to each trial independently.** Nothing in a contract reaches across trials: an aggregate limit sums occurrences within one trial, participation scales that trial's loss, a filter keeps that trial's covered events. Trial 7 never references Trial 8.[^per-trial] This independence lets us decompose the calculation to the level of a single trial, where each term's effect is transparent, and it means the whole distribution is the same four-term evaluation fanned out over every trial and collected:

```mermaid
graph TB
    Y["SunCoast YELT<br/>20 trials"]:::Losses --> T1["Trial 1"]:::Losses
    Y --> T9["Trial 9"]:::Losses
    Y --> T20["Trial 20"]:::Losses
    T1 --> E1("filter → period → occurrence layer → aggregate limit"):::Process
    T9 --> E9("filter → period → occurrence layer → aggregate limit"):::Process
    T20 --> E20("filter → period → occurrence layer → aggregate limit"):::Process
    E1 --> G1["gross · $0"]:::Metric
    E9 --> G9["gross · $60M"]:::Metric
    E20 --> G20["gross · $0"]:::Metric
    G1 --> D["Collect and sort → Contract 1's EP curve"]:::Metric
    G9 --> D
    G20 --> D

    classDef Losses fill:#F5CEBC,stroke:#B16C4B,stroke-width:1.5px,color:#2B2620
    classDef Process fill:#F7CBCB,stroke:#B46669,stroke-width:1.5px,color:#2B2620
    classDef Metric fill:#BDE2CB,stroke:#459269,stroke-width:1.5px,color:#2B2620
```

No trial's evaluation waits on another's, so the trials can run in any order or all at once.

:::tip[Intuition]
That independence is why we can understand the whole computation by watching a single trial. A contract's loss distribution is "apply the contract to every trial, then collect the gross losses and sort them": the [exceedance probability (EP) curves](https://reinsurance.dev/toolkit/distributions/) from the previous chapter are nothing more than this per-trial evaluation repeated 20 times on the demo tier, or 10,000 to 1,000,000+ times in production. Understand one trial and you understand the mechanism; the distribution is the same evaluation, collected and sorted.
:::

:::note[In Practice]
Per-trial independence is also why this computation is **embarrassingly parallel**.[^per-trial] Each trial can be evaluated on a separate core, machine, or partition with very little coordination, because no trial's result depends on another's. That is the property production systems rely on when scaling evaluation to [10,000 to 1,000,000+ trials](https://reinsurance.dev/toolkit/trials/#demo-scale).
:::

[Applying Contract 1's terms](https://reinsurance.dev/financial-modelling/dissection/#dissecting-a-catxol) step by step to one trial, **Trial 9** of SunCoast's YELT, shows how the losses change at each stage.

## Dissecting a CatXoL

**Contract 1** is the CatXoL in SunCoast Insurance's reinsurance program. **Caldera Re** takes a **14.5% participation** in the layer, sharing it with other reinsurers. Its terms:

| Term | Value |
| --- | --- |
| Covered loss | Florida hurricane (peril = HU, geography = FL) |
| Contract period | 2024-01-01 to 2024-12-31 |
| Occurrence layer | \$30M limit excess of \$10M attachment |
| Reinstatements | 1 |
| Participation (Caldera Re's share) | 14.5% |

In contract language: *"\$30M excess of \$10M each and every Florida hurricane, with one reinstatement, for the 2024 year, at 14.5%."*[^each-and-every] Each clause of that wording is one financial term, and the steps apply those terms to Trial 9 in composition order.

### Step 0: the subject losses

Trial 9 is one simulated year of SunCoast's book, 30 occurrences across two perils: earthquakes in California and Arizona, and hurricanes along the Gulf and Atlantic coasts.

*Figure (interactive on the web page): Trial 9 subject losses: 30 occurrences, \$272.1M total, one bar per occurrence at its event date (height is the loss amount, colour the peril). Two large Florida hurricanes (\$102.4M in September and \$80.8M in October) dominate a year otherwise made of small earthquakes and non-Florida storms.*

The full subject total is \$272.1M. Contract 1 will not see most of it: it covers only Florida hurricane, and only a slice of that. Each term can only narrow what reaches the reinsurer.

| Peril | Occurrences | Subject loss |
| --- | ---: | ---: |
| Earthquake (CA / AZ) | 22 | \$27.6M |
| Hurricane (all geographies) | 8 | \$244.5M |
| &emsp;of which Florida hurricane | 4 | \$216.8M |
| **Total** | **30** | **\$272.1M** |

### Step 1: filter to covered losses

The contract covers *Florida hurricane*. The first term is a **[filter](https://reinsurance.dev/glossary/#loss-filter)**: keep only occurrences whose peril is HU and geography is FL; drop everything else.

*Figure (interactive on the web page): Step 1: filter to Florida hurricane. Four occurrences survive (\$29.7M, \$102.4M, \$4.0M, \$80.8M); the other 26 are dimmed out. FL HU subject total: \$216.8M.*

Four occurrences survive, a \$216.8M Florida hurricane subject total.[^rounding] Note that the colour encodes peril alone, so the filter also has to drop the Texas and Louisiana hurricanes (same colour, wrong geography). We [formalise the filter term in the catalog](https://reinsurance.dev/financial-modelling/terms/filter/).

| Surviving occurrence | Subject loss |
| --- | ---: |
| Jul 12, FL hurricane | \$29.7M |
| Sep 6, FL hurricane | \$102.4M |
| Sep 6, FL hurricane | \$4.0M |
| Oct 5, FL hurricane | \$80.8M |
| **Total** | **\$216.8M** |

### Step 2: apply the contract period

The **[contract period](https://reinsurance.dev/glossary/#contract-period)** term keeps only events within the inception-to-expiry window (here, 2024-01-01 to 2024-12-31). All four Florida hurricanes fall inside it, so for Trial 9 the set of surviving occurrences is unchanged. In a different year (a mid-year inception, or a storm in January 2025), events outside the window would be dropped at this step. The [contract period](https://reinsurance.dev/financial-modelling/terms/contract-period/) catalog page formalises it.

| Contract period | Occurrences | Subject |
| --- | ---: | ---: |
| Within 2024-01-01 – 2024-12-31 | 4 of 4 | \$216.8M |

### Step 3: apply the occurrence layer

These are the terms that shape the payout. *\$30M excess of \$10M, each and every occurrence*: for every Florida hurricane the contract ignores the first \$10M (the **[attachment](https://reinsurance.dev/glossary/#attachment-point)**) and pays the next \$30M (the **[limit](https://reinsurance.dev/glossary/#limit)**). Anything above \$40M returns to the cedent's retention. Formally, the occurrence gross for each event is:

$$
L^{\text{gross}}_{\text{occ}} = \min(\max(L^{\text{subject}} - 10,\; 0),\; 30)
$$

The contract pays the part of each occurrence that falls inside the layer; what lies below the attachment (\$10M) and above the top of the layer (\$40M) stays with the cedent as its retained loss. The [occurrence excess](https://reinsurance.dev/financial-modelling/terms/occurrence-excess/) catalog page formalises this term.

*Figure (interactive on the web page): Step 3: occurrence layer, \$30M xs \$10M. The \$102.4M and \$80.8M storms exceed the limit and cap at \$30M each; the \$29.7M storm pays \$19.7M; the \$4.0M storm does not reach the \$10M attachment and pays nothing.*

- \$102.4M and \$80.8M both exceed the \$30M limit → each caps at \$30M
- \$29.7M falls inside the layer → pays \$29.7M − \$10M = \$19.7M
- \$4.0M does not reach the \$10M attachment → pays nothing

The occurrence layer is applied to each event independently; the per-occurrence results sum to \$79.7M.

| Occurrence | Subject | Occurrence gross |
| --- | ---: | ---: |
| Jul 12 | \$29.7M | \$19.7M |
| Sep 6 | \$102.4M | \$30.0M |
| Sep 6 | \$4.0M | \$0.0M |
| Oct 5 | \$80.8M | \$30.0M |
| **Total** | \$216.8M | **\$79.7M** |

### Step 4: apply the aggregate limit

The number of **[reinstatements](https://reinsurance.dev/glossary/#reinstatement)** is how many times the occurrence layer's limit can be restored once it is exhausted, so a layer with $n$ reinstatements can pay its limit $n + 1$ times in total: the original limit, plus [one restoration per reinstatement](https://reinsurance.dev/financial-modelling/contracts/catxol/#reinstatements-as-the-aggregate-limit).[^reinstatements] Mathematically, that is an aggregate limit of $(n + 1) \times \ell$, a claims-side encoding that sets the reinstatement premium mechanics aside (the [CatXoL](https://reinsurance.dev/financial-modelling/contracts/catxol/#reinstatements-as-the-aggregate-limit) page spells out what it drops). With one reinstatement, that is $(1 + 1) \times 30\text{M} = 60\text{M}$. Sum the per-occurrence payouts in event order and cap the running total at \$60M.

*Figure (interactive on the web page): Step 4: aggregate limit, \$60M. Bars are the per-occurrence gross from step 3. The cumulative line climbs to \$49.7M after the July and September storms, then the October storm would push it to \$79.7M, but the aggregate limit clips it at \$60M.*

The cumulative line climbs to \$49.7M after the July storm and the two September storms (one of which did not reach the occurrence attachment and contributes nothing). The October hurricane's \$30M payout would push the total to \$79.7M, but the aggregate limit clips it at \$60M. The contract has exhausted its cover; the \$19.7M of the final event that exceeds the remaining aggregate capacity is retained by SunCoast. **The aggregate limit binds.** The [aggregate excess](https://reinsurance.dev/financial-modelling/terms/aggregate-excess/) catalog page formalises the term, including how the [aggregate limit](https://reinsurance.dev/glossary/#aggregate-limit) erodes occurrence by occurrence.

| Occurrence | Occurrence gross | Cumulative | After \$60M cap |
| --- | ---: | ---: | ---: |
| Jul 12 | \$19.7M | \$19.7M | \$19.7M |
| Sep 6 | \$30.0M | \$49.7M | \$49.7M |
| Sep 6 | \$0.0M | \$49.7M | \$49.7M |
| Oct 5 | \$30.0M | \$79.7M | \$60.0M |
| **Trial gross** | | | **\$60.0M** |

### Step 5: participation at roll-up

Caldera Re does not hold the entire layer: it takes a **14.5% [participation](https://reinsurance.dev/glossary/#participation)**, sharing the contract with other reinsurers. Participation is not one of the contract's terms (the composition ends at the aggregate limit, [defining the contract at 100%](https://reinsurance.dev/financial-modelling/composition/#composing-contracts-from-the-blocks)) and enters as a [scaling](https://reinsurance.dev/financial-modelling/terms/scaling/) at [portfolio roll-up](https://reinsurance.dev/financial-modelling/portfolios/#the-roll-up-participation-then-sum): multiply the gross loss by the reinsurer's share. For Trial 9, the \$60M standalone layer gross becomes Caldera Re's booked share of $0.145 \times 60\text{M} = 8.7\text{M}$.

| Trial 9 | Standalone (100% layer) | Caldera Re's share (14.5%) |
| --- | ---: | ---: |
| Gross loss | \$60.0M | \$8.7M |

The \$60M figure (the full layer, before participation) is what appears for Contract 1's standalone metrics and in the [pipeline distribution below](https://reinsurance.dev/financial-modelling/dissection/#zooming-back-out); Caldera Re's \$8.7M booked share enters only once the contract joins the [portfolio](https://reinsurance.dev/financial-modelling/portfolios/#the-roll-up-participation-then-sum).

### The whole sequence

Four financial terms, applied in sequence, turn Trial 9's \$272.1M of subject loss into a **\$60M standalone gross loss**, of which Caldera Re books its 14.5% share, **\$8.7M**, at roll-up:

```mermaid
%%{init: {'flowchart': {'defaultRenderer': 'elk'}}}%%
graph LR
    SUBJ["Subject<br/>$272.1M · 30 occ"]:::Losses
    FILT["Filter<br/>HU ∧ FL<br/>→ $216.8M · 4 occ"]:::Contract
    TIME["Contract period<br/>within 2024<br/>→ no change"]:::Contract
    OCC["Occurrence layer<br/>30M xs 10M<br/>→ $79.7M"]:::Process
    AGG["Aggregate limit<br/>cap at 60M<br/>→ $60M standalone"]:::Process
    PART["Participation<br/>× 14.5% at roll-up<br/>→ $8.7M booked"]:::Metric

    SUBJ --> FILT --> TIME --> OCC --> AGG --> PART

    classDef Losses fill:#F5CEBC,stroke:#B16C4B,stroke-width:1.5px,color:#2B2620
    classDef Contract fill:#BFDBF8,stroke:#4F84BA,stroke-width:1.5px,color:#2B2620
    classDef Process fill:#F7CBCB,stroke:#B46669,stroke-width:1.5px,color:#2B2620
    classDef Metric fill:#BDE2CB,stroke:#459269,stroke-width:1.5px,color:#2B2620
```

The runnable version evaluates the four contract terms on Trial 9, applies the roll-up participation, and reproduces the numbers above:

Python (`python/caldera_re/contract1_trial9.py`, runnable on the web page):

```python
"""Single-trial dissection of Contract 1 (SunCoast FL HU CatXoL, 30M xs 10M,
1 reinstatement) on Trial 9 of the Caldera Re SunCoast YELT.

This is the reconciliation source for the single-trial walkthrough on the
Financial modelling page. It prints, for Trial 9:

  * the full subject loss timeline (every occurrence, all perils)
  * each financial term applied in sequence and its effect
  * the per-occurrence and aggregate arrays the page's timeline charts use

Run with:
    cd python && PYTHONPATH=. uv run --with numpy --with pandas --with pyyaml \
        python3 caldera_re/contract1_trial9.py
"""

import pandas as pd

from caldera_re.data import cedents
from caldera_re.report import table

S = 9                       # the trial we dissect
A, L = 10.0, 30.0           # attachment, occurrence limit ($M)
REINSTATEMENTS = 1
AGG_LIMIT = L * (1 + REINSTATEMENTS)   # 60M
PARTICIPATION = 0.145   # Caldera Re's share of the layer, applied at roll-up

def occurrences(rows):
    """Collapse YELT rows to one row per occurrence (event), summing the loss
    across geographies and lines of business. Each event has a single peril."""
    return (rows.groupby(['timestamp', 'event_id', 'peril'], as_index=False)
            .agg(loss=('loss', 'sum'),
                 geos=('geography_id', lambda g: '/'.join(sorted(set(g))))))

if __name__ == '__main__':
    sc = cedents.suncoast
    s9 = sc[sc['trial_id'] == S].copy()

    # --- Subject: every occurrence in the trial, all perils ---
    subj = occurrences(s9).sort_values('timestamp').reset_index(drop=True)
    print(f"Trial {S}: {len(s9)} YELT rows -> {len(subj)} occurrences "
          f"({(subj.peril == 'HU').sum()} HU, {(subj.peril == 'EQ').sum()} EQ)\n")

    print("Subject loss timeline (all occurrences):")
    table(['date', 'event', 'peril', 'geo', 'loss'],
          subj[['timestamp', 'event_id', 'peril', 'geos', 'loss']],
          fmt=['{}', '{}', '{}', '{}', '{:.2f}M'])
    print(f"\n  Subject total (all perils): {subj['loss'].sum():.1f}M")

    # --- Step 1: Filter YELT rows to covered peril + geography (FL HU) ---
    covered_rows = s9[(s9['peril'] == 'HU')
                      & (s9['geography_id'] == 'FL')]
    covered = occurrences(covered_rows).sort_values('timestamp').reset_index(drop=True)
    print(f"\nStep 1 - Filter (peril=HU, geography=FL): "
          f"{len(subj)} -> {len(covered)} occurrences")
    print(f"  FL HU subject total: {covered['loss'].sum():.1f}M")
    print(f"  losses: {[round(x, 2) for x in covered['loss']]}")

    # --- Step 2: Contract period (2024 full year) ---
    ts = pd.to_datetime(covered['timestamp'])
    rows_before_period = len(covered)
    covered = covered[(ts >= '2024-01-01') & (ts <= '2024-12-31')].copy()
    print(f"\nStep 2 - Contract period (2024-01-01 .. 2024-12-31): "
          f"{rows_before_period} -> {len(covered)} (no change for T{S})")

    # --- Step 3: Occurrence excess (30M xs 10M), per occurrence ---
    covered['gross_occ'] = (covered['loss'] - A).clip(lower=0).clip(upper=L)
    print(f"\nStep 3 - Occurrence excess ({L:.0f}M xs {A:.0f}M):")
    table(['date', 'subject', 'gross'],
          covered[['timestamp', 'loss', 'gross_occ']],
          fmt=['{}', '{:.2f}M', '{:.2f}M'])
    print(f"  per-occurrence gross total: {covered['gross_occ'].sum():.2f}M")
    print(f"  gross array: {[round(x, 2) for x in covered['gross_occ']]}")

    # --- Step 4: Aggregate cap (60M = (1 + 1) x 30M) ---
    covered['cumulative'] = covered['gross_occ'].cumsum()
    covered['cumulative_capped'] = covered['cumulative'].clip(upper=AGG_LIMIT)
    agg = covered['gross_occ'].sum()
    capped = min(agg, AGG_LIMIT)
    print(f"\nStep 4 - Aggregate cap ({AGG_LIMIT:.0f}M, {REINSTATEMENTS} reinst.):")
    table(['date', 'gross', 'cum', 'cum capped'],
          covered[['timestamp', 'gross_occ', 'cumulative',
                   'cumulative_capped']],
          fmt=['{}', '{:.2f}M', '{:.2f}M', '{:.2f}M'])
    print(f"  aggregate gross {agg:.2f}M -> capped {capped:.2f}M "
          f"({'binds' if capped < agg else 'no bind'})")

    # --- Step 5: Participation ---
    # Standalone metrics are reported at 100% (the full layer); Caldera Re's
    # participation share is applied at portfolio roll-up.
    booked = capped * PARTICIPATION
    print(f"\nStep 5 - Participation ({PARTICIPATION:.1%}):")
    print(f"  standalone gross (100% layer): {capped:.2f}M")
    print(f"  Caldera Re booked share:        {booked:.2f}M")

    print(f"\n=> Trial {S} Contract 1 standalone gross: {capped:.1f}M "
          f"(Caldera Re books {booked:.1f}M at {PARTICIPATION:.1%})")
```

### Zooming back out

Trial 9 was one trial. Run the same sequence of terms across all 20 trials of **Caldera Re**'s SunCoast [demo tier](https://reinsurance.dev/toolkit/trials/#demo-scale), collect the standalone gross losses, and sort them, and you have Contract 1's loss distribution (reported at 100%, before participation). Only the input losses differ between trials; the terms are the same, so every stage of the pipeline has its own [EP curve](https://reinsurance.dev/toolkit/distributions/), the loss distribution as of that term. Caldera Re's 14.5% participation scales the whole distribution down proportionally at roll-up.

*Figure (interactive on the web page): Contract 1 pipeline across all 20 trials, reported at 100% (before participation). Trial 9 (the trial we dissected) and Trial 19 are where the aggregate limit binds: toggle 'Standalone gross' to see it clip both to \$60M. Click the legend pills to toggle curves; hover to emphasise one.*

The dissection gave us the intuition: a contract is a pipeline of financial terms applied per trial. Next, [Contracts as compositions](https://reinsurance.dev/financial-modelling/composition/) formalises each term and shows how each contract type in the catalog is built from the same blocks.

[^rounding]: Every figure on the site is computed from the unrounded losses in the demo CSVs and displayed to one decimal, so displayed components can miss a displayed total by a unit in the last place. Here the four occurrences show as \$29.7M, \$102.4M, \$4.0M, and \$80.8M: \$216.9M as displayed, \$216.8M unrounded. The convention is stated in the [Caldera Re appendix](https://reinsurance.dev/caldera-re/#portfolio).

[^reinstatements]: IRMI, ["More Porridge Please: Reinstatements in Reinsurance"](https://www.irmi.com/articles/expert-commentary/more-porridge-please-reinstatements-in-reinsurance), Expert Commentary. A reinstatement restores an exhausted limit for the remainder of the period; a layer with one reinstatement can pay its limit twice.

[^per-trial]: Aman Bahl, Oliver Baltzer, Andrew Rau-Chaplin, and Blesson Varghese, ["Parallel Simulations for Analysing Portfolios of Catastrophic Event Risk"](https://arxiv.org/abs/1308.2066), Workshop at SC12, 2012 (co-authored by this site's author). The aggregate analysis stage is "executed for each Layer and each trial in the YET", and every implementation the paper benchmarks runs a single thread per trial, each performing "aggregate analysis for a single trial".

[^each-and-every]: United Property and Casualty Insurance Company, ["Property Catastrophe Excess of Loss Reinsurance Agreement"](https://www.sec.gov/Archives/edgar/data/1401521/000140152112000027/exh10426jun12.htm), SEC EDGAR exhibit 10.4, 2012, whose first layer is written in this shape: liability "in respect of each and every Loss Occurrence for the Ultimate Net Loss over and above an initial Ultimate Net Loss of \$15,000,000 each and every Loss Occurrence, subject to a limit of liability to the Reinsurer of \$10,000,000 each and every Loss Occurrence". Guy Carpenter, [Glossary of Reinsurance Terms](https://www.guycarp.com/company/news-and-events/glossary.html), records "each and every" as "an expression used in treaties and certificates". The wording names the loss occurrence where this site names the covered peril.
