How the cash-flow model works
Data model · Cash-flow model · Ceding a loss distribution through contract structures · The reinsurance suite
This page follows one acceptance through the cash-flow cube with real numbers. It then opens up the section engine that handles quota shares, excess-of-loss layers, aggregate layers and inuring. Every figure here is pinned by a test, either Reinsurance/Acceptance/Test/CessionTests.cs or Reinsurance/Acceptance/Test/CededClaimsTests.cs. If the code changed, a test would turn red before this page went out of date.
Two live walkthroughs run the same model in a kernel on the page: The Cash-Flow Cube and Ceding Through an Acceptance.
Part 1 — follow one acceptance
The inputs
The acceptance is Property Cat 40 xs 60 — UWY 2023: AttachmentPoint 60, Cover 40, PremiumRate 0.25, Share 1.0, in USD.
Subscribers see this result live; below is the same worked example as a static table.
| Property Cat 40 xs 60 — UWY 2023 | |
|---|---|
| Line of business | Property |
| Underwriting year | 2023 |
| Currency | USD |
| Attachment point | 60 |
| Cover | 40 |
| Rate on line | 25 % |
| Share | 100 % |
| Premium (rate on line × cover) | 10 |
The book it protects is the Property / US / underwriting year 2023 segment. Five Cashflow nodes ship for it, all in millions of USD:
| Node | CashflowType | DevelopmentDate | Value |
|---|---|---|---|
| Property US 2023 — Premium | Premium | 2023-03-31 | +12.0 |
| Property US 2023 — Commission | Commission | 2023-03-31 | −3.0 |
| Property US 2023 — Paid Loss dev 2023 | PaidLoss | 2023-12-31 | −20.0 |
| Property US 2023 — Paid Loss dev 2024 | PaidLoss | 2024-12-31 | −50.0 |
| Property US 2023 — Paid Loss dev 2025 | PaidLoss | 2025-12-31 | −20.0 |
The sign convention comes from Reinsurance/Cashflow/Source/Cashflow.cs: an inflow to the (re)insurer is positive (premium), and an outflow is negative (commission, paid loss). DevelopmentDate is the development axis: the same underwriting year 2023 pays out in 2023, 2024 and 2025.
The cession scope reads the paid losses as positive gross amounts per development year (CashflowStorage.GrossPaidByDevYear): 2023 → 20, 2024 → 50, 2025 → 20.
Step 1 — cumulative gross loss
Reinsurance/Acceptance/Source/Cession.cs, scope CededCashflows (its identity is the development year):
double GrossCumulative =>
GetStorage().GrossPaidByDevYear.Where(kv => kv.Key <= Identity).Sum(kv => kv.Value);
- 2023:
20 - 2024:
20 + 50 = 70 - 2025:
20 + 50 + 20 = 90
Step 2 — what the layer has paid so far
double CededCumulative =>
Math.Min(GetStorage().Acceptance.Cover,
Math.Max(0, GrossCumulative - GetStorage().Acceptance.AttachmentPoint));
- 2023:
min(40, max(0, 20 − 60)) = min(40, 0) = 0. The loss is still below the attachment. - 2024:
min(40, max(0, 70 − 60)) = min(40, 10) = 10. The loss pierces the attachment. - 2025:
min(40, max(0, 90 − 60)) = min(40, 30) = 30.
The layer would be exhausted at 60 + 40 = 100. This book stops at 90, so 10 of cover remains unused.
Step 3 — the ceded flow of each year
A flow is the increment of the cumulative. The scope reads the previous year from its own earlier result (GetScope<CededCashflows>(Identity - 1)):
double CededFlow =>
CededCumulative
- (GetStorage().GrossPaidByDevYear.ContainsKey(Identity - 1)
? GetScope<CededCashflows>(Identity - 1).CededCumulative
: 0);
- 2023:
0 − 0 = 0 - 2024:
10 − 0 = +10 - 2025:
30 − 10 = +20
Step 4 — the net flow the cedent keeps
double NetFlow => -GetStorage().GrossPaidByDevYear.GetValueOrDefault(Identity) + CededFlow;
- 2023:
−20 + 0 = −20 - 2024:
−50 + 10 = −40 - 2025:
−20 + 20 = 0. In 2025 the recovery fully offsets the payment.
The result
| Dev year | Gross paid | Gross cum | Ceded cum | Ceded flow | Net flow |
|---|---|---|---|---|---|
| 2023 | 20 | 20 | 0 | 0 | −20 |
| 2024 | 50 | 70 | 10 | +10 | −40 |
| 2025 | 20 | 90 | 30 | +20 | 0 |
The acceptance premium is PremiumRate × Cover = 0.25 × 40 = 10. The total recovery is 30, so the reinsurer's loss ratio is 30 / 10 = 300 % (CessionTests.AcceptanceResult_LossRatio300Percent).
That premium of 10 is computed from the acceptance's terms. It is not one of the Cashflow nodes. The +12 premium and −3 commission nodes are the segment's own flows.
A benign segment shows the other case. Casualty GB 2023 pays 1, 3 and 4 against a 10 xs 15 acceptance: the cumulative reaches 8 and never gets to 15. Nothing is ceded, and the net flow equals the gross (CessionTests.BenignSegment_NeverCedes).
Prerequisite: the class diagrams on this page are embedded from the Data Modelling plugin's explorer (/DataModelling/Explorer), which Reinsurance does not require. On an installation without it they render nothing; each type's own page under /Reinsurance still lists its properties.
Part 2 — the section engine
The CededCashflows scope handles one excess-of-loss layer applied to cumulative paid losses. Real programmes combine quota shares, per-claim layers, aggregate deductibles and limits, and sections that protect each other. The section engine in Reinsurance/Acceptance/Source/CededCashflows.cs handles these, working on TimeSeries cubes. A TimeSeries carries SectionId, ClaimsId, AmountType, Periodicity and Values[].
Two rules hold throughout the engine:
- Ceded amounts are stored negative, so netting is a plain sum:
net = gross + ceded. - Every amount type maps to a ceded counterpart.
IncurredIndividualClaimGrossNetbecomesIncurredIndividualClaimCeded,PaidAttritionalClaimGrossNetbecomesPaidAttritionalClaimsCeded, and so on (CessionAmountTypesinReinsurance/Acceptance/Source/CessionOperations.cs).
CededClaims (identity: an acceptance or section id) dispatches on what it finds:
| The id names | What happens |
|---|---|
an Acceptance |
sum of its SectionIds' ceded cubes |
an AggregateSection |
sum of its SubSectionIds' ceded cubes, then the AAD/AAL layer if set |
a ModelledReinsuranceSection with ProportionalSectionCover |
quota share on every gross-net row, then AAD/AAL if set |
a ModelledReinsuranceSection with NonProportionalSectionCover |
per-claim XL on individual claims only, then AAD/AAL if set |
GrossNetClaimsScope supplies the input. A stored series id returns the stored series. A modelled section aggregates its Models. When a model id is another section, that section's claims arrive net of its own cession. This is the inuring rule.
The worked book — Alpina 2025
Six individual claims plus an attritional bulk, incurred, gross 3,240:
| Claim | C1 | C2 | C3 | C4 | C5 | C6 | ATT (attritional) |
|---|---|---|---|---|---|---|---|
| Gross | 80 | 240 | 420 | 760 | 1,300 | 90 | 350 |
Quota share — 30 % with a claims limit of 300
Values = cf.Values.Select(v =>
-Math.Min(sectionCover.Cession * v, sectionCover.ClaimsLimit ?? double.MaxValue)).ToArray()
- C3:
−min(0.30 × 420, 300) = −126 - C5:
−min(0.30 × 1,300, 300) = −min(390, 300) = −300. The claims limit binds. - ATT:
−min(0.30 × 350, 300) = −105. A quota share also takes its share of the attritional bulk. - Total:
−(24 + 72 + 126 + 228 + 300 + 27 + 105) = −882
With 40 % and a limit of 350, the cession is −1,126 and the net is 3,240 − 1,126 = 2,114. Exactly one claim, C5, is capped.
Per-claim XL — 500 xs 250
Values = cf.Values.Select(v =>
-Math.Min(sectionCover.Limit, Math.Max(0, v - sectionCover.AttachmentPoint))).ToArray()
Only the individual-claim amount types reach this formula. The attritional bulk is filtered out first, because no single loss inside it is known.
- C2:
−min(500, max(0, 240 − 250)) = 0 - C3:
−min(500, 420 − 250) = −170 - C4:
−min(500, 760 − 250) = −min(500, 510) = −500 - C5:
−min(500, 1,300 − 250) = −500 - Total:
−1,170
The second layer, 500 xs 750, cedes C4 −10 and C5 −500, a total of −510.
The aggregate layer — AAD 200, AAL 800 around the 500 xs 250
CessionOperations.CedeIntoAggregateLayer groups the ceded rows by their top-level amount type and sums the year. The deductible erodes the total first, the limit caps what remains, and the adjustment is spread back over the claims pro rata. The formula as documented in the source:
yearCeded = Σ ceded // the year's cessions, summed (a negative number)
afterAad = min(0, yearCeded + AAD) // the deductible hands the first AAD back to the cedant
final = max(afterAad, −AAL) // the limit caps the year's total recovery
adjusted = ceded × final / yearCeded // …pushed back onto each claim, pro rata
With real numbers:
yearCeded = −170 − 500 − 500 = −1,170afterAad = min(0, −1,170 + 200) = −970final = max(−970, −800) = −800- C3:
−170 × 800 / 1,170 ≈ −116.2. C4 and C5:−500 × 800 / 1,170 ≈ −341.9each.
The order matters: the deductible applies before the limit caps. For a recovery of 4 with AAD 1 and AAL 2, the correct cession is 4 → 3 → 2. An earlier ordering gave 1, and the regression test AggregateLayer_RegressionAad1Aal2OnRecovery4_CedesExactly2 keeps that from coming back.
Inuring — who pays first
The programme has two sections. SecA is a 20 % quota share on the book. SecB is a 500 xs 250 whose LossModelMap points at SecA, so SecB sees the book net of SecA's cession.
- SecA cedes 20 % of everything:
−(16 + 48 + 84 + 152 + 260 + 18 + 70) = −648. - SecB sees the QS-net book: C3
336, C4608, C51,040, ATT280, … - SecB cedes C3
−min(500, 336 − 250) = −86, C4−min(500, 608 − 250) = −358, C5−500, for a total of−944. - The acceptance
ProgramQsFirstsums both:−648 − 944 = −1,592, leaving a net of3,240 − 1,592 = 1,648.
Flip the order and the XL sees the gross book first, ceding −1,170. The QS then takes 20 % of the XL-net 2,070, ceding −414, for a total of −1,584. The difference of 8 comes purely from who pays first.
Wrap SecA and SecB in an AggregateSection with AAD 150 and AAL 600. The total goes −1,592 → −1,442 → −600.
Development periods
The CededCashflows scope uses a development year as its identity, and each Cashflow fact carries a DevelopmentDate. Section-engine series carry a Periodicity (Yearly, Quarterly or Monthly) and treat Values as cumulative. When CessionOperations.Aggregate sums series of different lengths, it extends the shorter one by repeating its last value. The Alpina 2025 book in the tests has a single period per claim.
Part 3 — where the flows go
- Planning reads the Cashflow facts as actuals. The Variance type maps every
Reinsurance/Cashflowwhose type a plan line claims onto the plan grain. Premium Written declaresActualsCashflowType = Reinsurance/CashflowType/Premium, Loss declares…/PaidLossand Commission declares…/Commission. The financial year is the year of theDevelopmentDate, and currencies and countries are summed together. The variance isactual.Minus(plan), so an adverse loss year comes out negative (Planning/Variance/Source/PlanVsActual.cs). Planning reads the facts from the Reinsurance partition into its own copy of the record (Planning/Variance/Source/Cashflow.cs); that copy must stay identical to the original. - IFRS 17 does not read these flows. The IFRS 17 module declares no dependency on Reinsurance (
dependsOn: []). It measures its own cube:RawVariablenominal projections perGroupOfContract,AocType,EstimateTypeandAmountType, monthly from the reporting date. Its gross / reinsurance / net view is its ownPortfolioViewflags enum. In the demo, the MTPL liability for incurred claims is generated from the claims declared in the demo spine (ReinsuranceDemo/SampleData/Source/Ifrs17Spine.cs), not fromReinsurance/Cashflownodes. - The ceded results are computed, not stored. No module writes ceded flows back as Cashflow nodes; the shipped CashflowType members are only Premium, Commission and PaidLoss. Today the scopes are evaluated by the Tests areas, by the
CessionStudyarea behind Ceding a loss distribution through contract structures (2,000 simulated years through the realCededClaimsengine), and by the cells of Ceding Through an Acceptance, which repeats the same formula inline.
Check yourself
Question. Keep the 40 xs 60 acceptance, but suppose 2025 paid 40 instead of 20. What are the 2025 ceded cumulative, ceded flow and net flow?
Answer. The gross cumulative is 20 + 50 + 40 = 110. The ceded cumulative is min(40, max(0, 110 − 60)) = min(40, 50) = 40: the layer is exhausted. The ceded flow is 40 − 10 = +30, and the net flow is −40 + 30 = −10. The cedent carries everything above the exhaustion point of 100.
Reference
| Formula (as in source) | Where | Pinned by |
|---|---|---|
GrossCumulative = Σ GrossPaidByDevYear[k ≤ Identity] |
Acceptance/Source/Cession.cs |
CessionTests.GrossCumulative_Develops |
CededCumulative = Math.Min(Cover, Math.Max(0, GrossCumulative - AttachmentPoint)) |
same | CededCumulative_PiercesAttachmentIn2024 |
CededFlow = CededCumulative − previous CededCumulative |
same | CededFlows_AreIncrements |
NetFlow = −gross paid + CededFlow |
same | NetFlows_RecoveryOffsetsIn2025 |
QS: -Math.Min(Cession * v, ClaimsLimit ?? double.MaxValue) |
Acceptance/Source/CededCashflows.cs |
Qs30_TakesItsSliceOfEveryAmountType |
XL: -Math.Min(Limit, Math.Max(0, v - AttachmentPoint)) (individual claims only) |
same | Xl500xs250_AttritionalStaysNet |
| AAD then AAL, pushed back pro rata | Acceptance/Source/CessionOperations.cs |
AggregateLayer_* |
| Inuring: a model id that names a section yields that section's net | CededCashflows.cs (GrossNetClaimsScope) |
Program_QsInuresToTheXl, Program_FlippedInuringOrder |
Premium at 100 % = Cover × PremiumRate |
Acceptance/Source/AcceptanceReport.cs |
AcceptanceResult_LossRatio300Percent |
Next: Ceding a loss distribution through contract structures · The Reinsurance data model · The reinsurance suite