Loss Modelling — the formulas
This page works through one deal by hand: the demo stop-loss model. It is an employer group of 1,000 members, a specific deductible of 150,000, a max reimbursement of 2,000,000, a 12/15 contract basis, a 90 % share and 3 policy years. We take each formula in the order the engine runs it. Then we do the same for its twin on the claim-duration engine, DurationDemo.
Every formula below is quoted from source. File paths are given relative to LossModelling/. Where
a figure comes from a run rather than from a formula, the page says so and names where it was read.
Live views of the demo model:
- Cashflows (the default view)
- Result
- Population
- Summary
- Price
- On the duration twin: Duration and Summary
Subscribers see this result live; below is the same worked example as a static table.
| Demo model, 3 years, our 90 % share | Amount |
|---|---|
| Our premium | 2,290,160 |
| Recoveries | −1,464,538 |
| Costs | −526,737 |
| Result | 298,885 |
These are the Result area's figures for 400 simulations at seed 2026, read on 2026-11-03. The full table in the high-cost engine section further down adds the margin, the year-1 loss ratio and its spread.
Step 0 — which engine, and is there anything to compute?
StopLossEngines.Evaluate (StopLossModel/Source/StopLossEngines.cs) is the one evaluation.
The views draw it, and a stored price must come from it. It refuses before it computes:
| Check | Formula in source | Demo |
|---|---|---|
| a census | m.Census.Count == 0 \|\| Census.Lives(m.Census) == 0 refuses |
1,000 lives, so it passes |
| complete terms | t.Deductible <= 0 \|\| t.MaxReimbursement <= 0 \|\| t.AnnualPremium <= 0 refuses |
150,000 / 2,000,000 / 768,768, so it passes |
| a known engine | IsDuration(m) (engine = ConditionDuration) or IsHighCost(m) (empty or HighCost) |
empty, so HighCost |
| HighCost only: the deductible clears the trended threshold | floor = a.HighCostThreshold * Math.Pow(1 + a.MedicalTrend, years - 1); refuse if t.Deductible < floor |
100,000 × 1.085² = 117,722.50 < 150,000, so it passes |
The floor check exists because the engine draws claims only above the threshold. A deductible below it would miss the recoveries that fall between the deductible and the threshold.
The high-cost engine (StopLossSimulation.Run)
Source: StopLossModel/Source/StopLossSimulation.cs. The basis is the
US2026Demo health assumptions:
- threshold 100,000, Pareto α 2.2
- persistence ρ = 0.21, high-cost mortality multiplier 4
- trend 8.5 %, turnover 17 %
- onset share 0.7, reimbursement lag 0
- completion
[0.15, 0.55, 0.80, 0.90, 0.94, 0.96, 0.97, 0.98, 0.985, 0.99, 0.993, 0.996, 1.0]
Demo only. The set's own basis says: "Demo character only — not a pricing basis." Some of its values are sourced and some are estimates; the set's
basisfield names which. The same holds for the condition set. Every figure on this page illustrates the mechanics; none is a price.
Every run is seeded (new Random(seed), seed 2026) and clamped:
yearsto 1–10 (EffectiveYears)simulationsto 1–5,000paidMonthsto 12–36 (EffectivePaidMonths)
1. Premium and costs, on our share
yearPremium = terms.AnnualPremium * Math.Pow(1 + renewalIncrease, y) * terms.Share
monthly premium = yearPremium / 12
monthly costs = yearPremium / 12 * (terms.ExternalCostRatio + terms.InternalCostRatio)
On the demo:
| Policy year | yearPremium | costs (18 % + 5 % = 23 %) |
|---|---|---|
| 1 | 768,768 × 1.0 × 0.9 = 691,891.20 | 159,134.98 |
| 2 | 768,768 × 1.1 × 0.9 = 761,080.32 | 175,048.47 |
| 3 | 768,768 × 1.21 × 0.9 = 837,188.35 | 192,553.32 |
| Total | 2,290,159.87 | 526,736.77 |
The monthly premium in year 1 is 57,657.60. Net of costs it is 57,657.60 × 0.77 = 44,396.35.
2. Who is high-cost: the stationary share, persistence and incidence
The census is spread over single ages by Expand: each band's members are spread evenly over its
ages, and the remainder goes to the youngest ages. In year 0 each age and sex cell draws its high-cost
members as Binomial(normal, Share(a, age, sex)), where Share is the band's stationary high-cost
share.
Worked: the expected number of high-cost members at the start is
Σ (members × share) over the census. That is 24 × 1.6 % + 24 × 1.4 % + … + 18 × 2.25 % = 8.98,
about 0.9 % of 1,000. The test ThePopulation_HoldsItsHeadcount pins "between 3 and 25 a year".
At each anniversary a high-cost member stays high-cost with probability ρ. A normal member falls ill with the incidence that keeps the share stationary:
incidence = share >= 1 ? 1 : share * (1 - a.Persistence) / (1 - share) // i = s(1−ρ)/(1−s)
Worked, males 40–49 (s = 1.15 %): i = 0.0115 × 0.79 / 0.9885 = 0.919 % a year.
Check the stationarity: next year's share is s·ρ + (1−s)·i = 0.0115 × 0.21 + 0.9885 × 0.00919 = 0.0115.
3. Deaths, leavers and replacements
At year end, for each age and sex cell:
q = band.MaleMortality | band.FemaleMortality
dn = Binomial(normal, q); dh = Binomial(high, Math.Min(1, q * a.HighCostMortalityMultiplier))
ln = Binomial(normal - dn, a.Turnover); lh = Binomial(high - dh, a.Turnover)
Then Replace runs: everyone ages one year, and the gone = dn + dh + ln + lh replacements are
hired at the starting age mix. Each cohort is high-cost at its age's stationary share. That keeps
both the headcount and the high-cost share stable. The test TheHighCostShare_StaysStationary_UnderTurnover
pins this.
Worked, males 40–49: q = 0.32 %, and 1.28 % for a high-cost member (× 4). About 17 % of the survivors leave each year.
4. One high-cost member's claims (Recover)
The annual claim is drawn from a Pareto above the threshold and trended:
claims = severity.Sample(rng) * Math.Pow(1 + a.MedicalTrend, y) // severity = Pareto { X0 = threshold, Alpha = α }
Pareto.Sample: X0 / Math.Pow(1.0 - U, 1.0 / Alpha)
A share of the claim, OnsetShare, comes as a lump in the onset month. The rest accrues evenly to
month 11. A member who persists from last year starts at month 0. A new one starts at a uniform
month, rng.Next(0, 12).
accrued[onset] = claims * lump; accrued[k] += claims * (1 - lump) / (12 - onset) for k = onset..11
cap = Math.Min(claims - deductible, maxReimbursement)
excess_k = Math.Min(Math.Max(running - deductible, 0) - Math.Max(before - deductible, 0), cap - excessSoFar)
paid at month k + j + lag = excess_k * (Completion[j] - Completion[j-1]) — counted only if k + j + lag < paidMonths
Worked by hand: take one member who falls ill in month 3 with a claim of 400,000, in year 0.
| Month k | accrued | excess over 150k incurred | reimbursed by month 14 (× 0.9 share) |
|---|---|---|---|
| 3 | 280,000 + 13,333.33 = 293,333.33 | 143,333.33 | 143,333.33 × Completion[11] = 0.996, so 128,484.00 |
| 4 | 13,333.33 | 13,333.33 | × 0.993, so 11,916.00 |
| 5 … 11 | 13,333.33 each | 13,333.33 each | × 0.99, 0.985, 0.98, 0.97, 0.96, 0.94, 0.90 |
| Total | 400,000 | 250,000 = 400,000 − 150,000 (the 2m cap does not bind) | 221,100.00 |
On a 12/12 basis (paidMonths = 12) the same claim reimburses only 202,065.00. The ratio is
0.91: the run-out is what the longer window buys. The test ThePaidWindow_AndTheDeductible_Bind
pins the ordering 12/12 < 12/15 ≤ 12/24. It also pins that 12/12 recovers 0.70–0.92 of 12/15 over a
whole run; the filed contract-basis factors imply about 0.84.
The laser is the known claimant. In year 0 it is drawn from the same Pareto, starts at month 0,
and uses its own deductible, Math.Max(laser.Deductible, terms.Deductible) = 400,000.
Worked: a laser claim of 600,000 reimburses 176,139.00 on our share by month 14.
5. The Pareto analytics behind those draws
From Source/Pareto.cs:
GetMean = Alpha * X0 / (Alpha - 1) // α > 1
GetVariance = X0² · α / ((α − 1)² · (α − 2)) // α > 2
S(x) = (X0 / x)^α
With X₀ = 100,000 and α = 2.2:
- E = 183,333.
- Variance: 2.2 > 2, so it is finite.
- P(X > 150,000) = (100/150)^2.2 = 41.0 %: about four in ten high-cost members pierce the deductible.
- Analytic expected layer loss, for one ground-up claim in the 2m xs 150k layer: E[min((X−150k)⁺, 2m)] = X₀α · (d(1−α) − (d+L)^(1−α)) / (α−1) = 49,130. This is a textbook check on the distribution the engine samples. The engine itself only samples; it does not compute this figure.
6. The result, the spread and the stored price
LossRatio (per year) = Recoveries / Premium
ratios = first-year recoveries of each simulation / first-year premium, sorted
LossRatioP10/P50/P90 = Percentile(ratios, q) = sorted[Math.Clamp((int)Math.Ceiling(q * n) - 1, 0, n - 1)] // nearest rank
ProbabilityOfRecovery = count(first-year recoveries > 0) / simulations
TotalResult = TotalPremium - TotalCosts - TotalRecoveries
Margin (stored) = TotalPremium > 0 ? TotalResult / TotalPremium : 0 // StopLossPricing.Summarize
CumulativeNet[t] = Σ_{τ≤t} (premium·(1 − cost ratios) − recoveries), averaged; P10/P90 per month
The Demo model's actual figures were read from the live Result area on the public instance on 2026-11-03: 400 simulations, seed 2026.
| Figure | Value | How it closes |
|---|---|---|
| Our premium, 3 years | 2,290,160 | = the hand total above |
| Recoveries | −1,464,538 | simulated |
| Costs | −526,737 | = 23 % of premium, the hand total above |
| Result | 298,885 | 2,290,160 − 1,464,538 − 526,737 |
| Margin | 13.1 % | 298,885 / 2,290,160 |
| Expected loss ratio, year 1 | 60.3 % | simulated year-1 recoveries / 691,891 |
| Loss ratio P10 – P90 | 6.5 % – 140.4 % | nearest-rank percentiles over 400 runs |
| Chance of a recovery | 96.8 % | share of runs where year 1 recovers anything |
The stored price (StopLossPricingResult, StopLossModel/Source/StopLossPricing.cs) holds
exactly these figures. ExpectedLossRatio = r.ByYear[0].LossRatio; the other fields are copied from
the run, plus a Fingerprint. The fingerprint is the first 12 bytes, in hex, of the SHA-256 of
[EngineVersion, model-without-pricing-fields-and-name, terms, assumptions, (conditions, table on the duration engine)].
StopLossPricing.Decide maps the stored state to a step:
| Stored state | Request | Step |
|---|---|---|
result current (IsCurrent) |
none | None |
| result current | Price |
ClearRequest |
| anything not current | Price |
Price, even right after a failed attempt on the same inputs |
not current; LastAttemptFingerprint ?? Result?.Fingerprint differs from the current fingerprint (a stale result, or inputs repaired after a failure) |
none | Price |
not current; the last attempt was on these same inputs (for example a failed attempt that wrote a PricingNote) |
none | None: it waits for a request |
| never priced or attempted (both fingerprints null) | none | None |
The shipped Demo node carries no stored result: its Price area reads Not priced yet. The test
TheStoredResult_EqualsTheViewsNumbers pins that a stored price equals the view's figures.
The claim-duration engine (ClaimDurationSimulation.Run)
Source: StopLossModel/Source/ClaimDurationSimulation.cs, with Source/ClaimDurationCurves.cs,
Source/ConditionAssumptions.cs and Source/MortalityTable.cs.
DurationDemo runs the same census and terms over
1 year, on:
- the condition set US2026Demo: eight groups, completion as above, lag 0;
- the NCHS 2024 mortality table.
Before it runs, Evaluate also refuses four inputs:
- a condition set with no groups;
- a negative lag;
- an unusable completion pattern;
- a continuation curve that does not start at 1, leaves 0–1, or increases (
ContinuationProblem);
and it refuses a mortality table that lacks a sex or has a q outside 0–1 (Mortality.Problem).
1. Onset: one draw over the groups
A member is eligible in a policy year only if they are alive and not on claim at the year's start. An eligible member starts at most one claim a year. Within one age × sex cell, the groups are drawn in order:
p = Math.Clamp(groups[g].IncidenceAt(attained, sex), 0, 1)
k = Binomial(remaining, Math.Clamp(p / left, 0, 1)); left -= p; remaining -= k
onset month = yearStart + rng.Next(0, 12)
IncidenceAt reads the band the age falls in. The oldest band holds past its end. An age in a gap
between bands gets 0.
Worked: Cardiovascular for a male aged 45 has an incidence of 0.25875 % a year. Transplant for a male aged 50 has 0.0975 %.
2. On claim, month by month: cost, death, recovery
incurred[t] = ((t == Start && !Prevalent ? g.OnsetCost : 0) + g.MonthlyCost) * Math.Pow(1 + g.CostTrend, t / 12) * level
level = CostLevel(cv) = Math.Exp(Math.Sqrt(s2) * z - s2 / 2), s2 = Math.Log(1 + cv * cv) // lognormal, mean 1; 1 when cv = 0
die if U < MonthlyDeath = Mortality.MonthlyQ(Mortality.Scaled(Mortality.Q(table, age, sex), g.ExcessMortality))
recover if U < RecoveryHazard(d) = 1 - S(d+1) / S(d) // the last step's hazard holds past the curve's end
else stay on claim; the claim stops at the cover's end (CutOff)
t / 12 is integer division, so the trend steps once per policy year.
The mortality arithmetic (Source/MortalityTable.cs):
MonthlyQ(q) = 1 - Math.Pow(1 - q, 1.0 / 12) // constant force; twelve months compound back to q
Scaled(q, m) = Math.Clamp(q * m, 0, 1)
Survival(x, n) = Π_{k<n} (1 - Q(x + k)) // ₙpₓ
CurtateExpectation = Σ_{k≥1} ₖpₓ ; CompleteExpectation = curtate + 0.5
Q(x, years) = q(x) · (1 − i(x))^years when the table has an improvement scale
Worked, the laser. The condition text is "NSTEMI myocardial infarction". Classify finds no exact
group id. It then takes the first group, in order, with a keyword in the text: Cancer has none, and
Cardiovascular matches "myocardial". The laser is male, aged 58.
- q(58, male) = 0.0090678, from the NCHS table.
- × excess mortality 5 = 0.045339.
- MonthlyQ = 1 − (1 − 0.045339)^(1/12) = 0.003859. Twelve months compound back to 0.045339.
- Recovery hazard = 1 − 0.65 / 1 = 0.35 a month. Cardiovascular's curve falls by 35 % a month, so the hazard is the same every month.
A laser is prevalent (Prevalent: true). It pays no onset cost, only 6,000 a month × its cost
level, against a 400,000 deductible. At level 1 that is about 2.8 months × 6,000 ≈ 17,000, far below
the deductible. Only a very high lognormal level (cv = 1) reaches the layer.
3. The analytic duration curve
ClaimDurationCurves.Curve runs the same death and recovery recursion without randomness and with
no cut-off, for 120 months:
on[d] += alive; dies = alive * MonthlyDeath(age + d/12); recovers = (alive - dies) * RecoveryHazard(d)
expected months on claim = Σ OnClaim; P(dies on claim) = Died[^1]
Both are finite-horizon quantities over the 120 months d = 0 … 119. Σ OnClaim adds up the months
spent on claim within that window. Died[^1] is the probability of having died by the start of
month 119.
With no onset mix the curve takes a claimant aged 50, male. The figures below are computed by hand with that recursion on the shipped US2026Demo set and the US2024 table:
| Group | Recovery hazard, month 0 | Monthly death at 50 M | Months on claim, accumulated over months 0–119 | P(died by the start of month 119) |
|---|---|---|---|---|
| Cancer | 0.10 | 0.010816 | 9.09 | 10.1 % |
| Cardiovascular | 0.35 | 0.002062 | 2.85 | 0.6 % |
| Transplant | 0.20 | 0.004173 | 4.92 | 2.1 % |
| Renal | 0.02 | 0.006333 | 35.40 | 26.8 % |
| Neonatal | 0.50 | 0.002062 | 2.00 | 0.4 % |
| Neuro | 0.15 | 0.001646 | 6.60 | 1.1 % |
| Specialty | 0.01 | 0.000614 | 67.64 | 5.5 % |
| Other | 0.25 | 0.001232 | 3.99 | 0.5 % |
At the horizon (month 119) a Specialty claimant still has about 27 % probability of being on claim, and a Renal claimant about 3 %. For those two groups the columns are truncated, not complete. Specialty and Renal are the long-running groups. They do recover, at a monthly hazard of only about 1 % and 2 % (their continuation curves fall by 1 % and 2 % a month), so most of their claims run until death or the cut-off. A truly chronic group would need a flat curve, with a hazard of 0. No shipped group has one.
4. Reimbursement and the run-out (Reimburse)
The policy year's 12 incurred months accumulate against the deductible. In year 0 that is the episode's own deductible (the laser's, or the terms'). In later years it is the terms' deductible, reset each year. Each month's excess is paid along the completion pattern:
excess_k = Math.Min(Math.Max(running - deductible, 0) - Math.Max(before - deductible, 0), maxReimbursement - excessSoFar)
paid = excess_k * (completion[j] - completion[j-1]) * share, at month k + j + lag
counted if at < paidMonths; otherwise LOST to the run-out (ClaimDurationResult.LostToRunOut)
Worked: one Transplant claim at cost level 1. It starts in month 2 and recovers after 5 months on claim.
| Month | incurred | excess over 150k | reimbursed by month 14 (× 0.9) |
|---|---|---|---|
| 2 | 250,000 + 12,000 = 262,000 | 112,000 | × Completion[12] = 1.0, so 100,800.00 |
| 3 | 12,000 | 12,000 | × 0.996, so 10,756.80 |
| 4 | 12,000 | 12,000 | × 0.993, so 10,724.40 |
| 5 | 12,000 | 12,000 | × 0.990, so 10,692.00 |
| 6 | 12,000 | 12,000 | × 0.985, so 10,638.00 |
| Total | 310,000 | 160,000 | 143,611.20 reimbursed + 388.80 lost to the run-out = 160,000 × 0.9 |
5. The duration result
The result has the same shape as the high-cost engine's: premium, recoveries, costs, the cumulative
net with its P10–P90 band, and the loss-ratio spread. On top of that, ClaimDurationResult adds, per
group:
ClaimantsPayingMonths(= months / episodes)- the shares ending
ByRecovery,ByDeathandByCutOff, which sum to 1 UncappedMonths(= Σ OnClaim of the curve over the simulated onset mix)Recoveries
It also adds a row per laser and LostToRunOut. Nobody is replaced on this engine:
Members = lives + modelled lasers − dead before the year, and Leavers = 0.
DurationDemo's actual figures were read from the live Summary area on the public instance on 2026-11-03:
- our premium 691,892 (= year 1 above)
- recoveries −350,418
- costs −159,135
- result 182,339 (margin 26.4 %)
- expected loss ratio, year 1 50.6 %
- P10 – P90 1.3 % – 117.4 %
- chance of a recovery 91.8 %
- mean months on claim 3.6
The frequency-severity model
Sources: FrequencySeverityLossModel/Source/FrequencySeveritySampler.cs,
DiscreteDistributionEstimator.cs and LinearTimeModelSampler.cs. One simulated period draws
N = Frequency.Sample(random), then N severities, and sums them:
AggregateAnnualLoss = Σ_{i=1..N} Severity.Sample(random)
E[S] = E[N] · E[X]
Var[S] = E[N] · Var[X] + Var[N] · E[X]²
Worked: this is the fixture of the test CompoundMean / CompoundVariance
(FrequencySeverityLossModel/Test/FrequencySeveritySamplerTests.cs): Poisson λ = 3, Pareto x₀ = 1,
α = 5.
- E = 5 × 1 / 4 = 1.25.
- Var = 1 × 5 / (4² × 3) = 5/48.
- E[S] = 3 × 1.25 = 3.75.
- Var[S] = 3 × 5/48 + 3 × 1.25² = 5.0.
300,000 seeded runs recover the mean within 3 % and the variance within 10 %; those are the test's tolerances.
The estimator. It keeps Σx and Σx². Its mean is Σx / n and its population variance is
Math.Max(0, Σx²/n − mean²). The test EstimatorMomentsExact pins {1, 2, 3, 4}: mean 2.5,
variance 1.25.
The compressed distribution sorts the samples into nPoints equal-count bins. Each point is a bin's
mean value at probability (i + ½)/nPoints. Quantile(p) interpolates linearly between bin centres
and clamps to the end bins.
The linear time model. One claim of severity X is spread as follows:
- incurred
[0, X/N_inc, 2X/N_inc, …, X], length N_inc + 1; - paid
[0, 0, X/N_paid, …, X], length N_paid + 2, one period behind.
Worked (test LinearMapShape): with N_inc = 3, N_paid = 5 and X = 100, incurred[1] = 33.33 and
paid[2] = 20.
Panjer: one knob for the frequency
Source/Panjer.cs is parameterised by its Mean and Q = Var / Mean, and dispatches on Q:
Q < 1 − 1e-9 → Binomial.WithMeanVariance(Mean, Mean * Q) // under-dispersed
Q > 1 + 1e-9 → NegativeBinomial.WithMeanVariance(Mean, Mean * Q) // over-dispersed
otherwise → Poisson { Lambda = Mean }
GetMean = Mean; GetVariance = Mean * Q
For Q < 1 these are target moments. Binomial.WithMeanVariance sets P = 1 − Q and
N = Mean / P, which is usually fractional. Binomial.Sample then draws (int)Math.Round(N)
trials (Source/Binomial.cs).
Worked: Panjer(10, 0.7) gives P = 0.3 and N = 33.33. The sampler draws 33 trials, so the sampled
mean is 33 × 0.3 = 9.9 and the sampled variance 33 × 0.3 × 0.7 = 6.93, against the targets
10 and 7. GetMean / GetVariance keep reporting 10 and 7.
Worked (test ClaimCountRecovery): Panjer.WithMeanQ(10, 2.0) resolves to a NegativeBinomial with
E[N] = 10 and Var[N] = 20. 200,000 seeded draws average within 2 % of 10.
Poisson itself is sampled by LossMath.NextPoisson: Knuth's product for λ < 30, and Hörmann's
transformed rejection (PTRS) above.
Check yourself
Q1. On the Demo model you lower the paid window from 12/15 to 12/12. By how much does the single 400,000 claim with onset month 3 lose, and why?
A1. It falls from 221,100.00 to 202,065.00, a drop of 19,035 or 8.6 %. With paidMonths = 12,
a payment at month k + j counts only if k + j < 12. The tail of the completion pattern for the
late months (from month 3 on) falls outside the window.
Q2. On DurationDemo, why does the lasered NSTEMI claimant add almost nothing to the expected recoveries, although the laser exists to flag a high-cost claimant?
A2. Three reasons, all in the source:
- The laser is
Prevalent, so it pays no onset cost (!e.PrevalentgatesOnsetCost). - Cardiovascular's monthly cost is 6,000, and its recovery hazard of 0.35 gives about 2.8 expected months on claim.
- The laser's deductible is
Math.Max(400,000, 150,000)= 400,000.
The episode costs about 17,000 at cost level 1, so only a very high lognormal level pierces it.
Reference
| Member | Formula as in source | File |
|---|---|---|
Evaluate high-cost floor |
a.HighCostThreshold * Math.Pow(1 + a.MedicalTrend, years - 1) |
StopLossModel/Source/StopLossEngines.cs |
MortalityPath |
model's → condition set's → LossModelling/MortalityTable/US2024 |
StopLossModel/Source/StopLossEngines.cs |
| year premium | AnnualPremium * (1 + renewal)^y * Share |
StopLossModel/Source/StopLossSimulation.cs |
| incidence | s * (1 - ρ) / (1 - s) |
StopLossModel/Source/StopLossSimulation.cs |
Recover |
lump + even accrual, capped excess, completion, paid window | StopLossModel/Source/StopLossSimulation.cs |
Percentile |
nearest rank ceil(q·n) − 1 |
StopLossModel/Source/StopLossSimulation.cs |
Binomial |
inversion for n·p < 30, normal approximation above | StopLossModel/Source/StopLossSimulation.cs |
Census.FromSummary |
normal spread (sd 11) over the working ages, largest remainder | StopLossModel/Source/StopLossModel.cs |
Summarize / Margin |
TotalResult / TotalPremium |
StopLossModel/Source/StopLossPricing.cs |
Fingerprint |
SHA-256 → 12 bytes hex over the inputs | StopLossModel/Source/StopLossPricing.cs |
CostLevel |
lognormal, mean 1, CV cv |
StopLossModel/Source/ClaimDurationSimulation.cs |
Reimburse |
capped excess, completion, lost to run-out | StopLossModel/Source/ClaimDurationSimulation.cs |
RecoveryHazard |
1 − S(d+1)/S(d) |
Source/ConditionAssumptions.cs |
Classify |
exact id → first keyword hit → DefaultGroup → last group |
Source/ConditionAssumptions.cs |
MonthlyDeath |
MonthlyQ(Scaled(Q(x), excess)) |
Source/ClaimDurationCurves.cs |
Curve |
deterministic on-claim / died / recovered recursion, 120 months | Source/ClaimDurationCurves.cs |
MonthlyQ, Survival, CurtateExpectation |
see above | Source/MortalityTable.cs |
Pareto Sample / GetMean / GetVariance |
X0·(1−U)^(−1/α), αX0/(α−1), X0²α/((α−1)²(α−2)) |
Source/Pareto.cs |
| compound moments | E[N]E[X], E[N]Var[X] + Var[N]E[X]² |
FrequencySeverityLossModel/Source/FrequencySeveritySampler.cs |
Panjer.Resolve |
Q < 1 Binomial · Q = 1 Poisson · Q > 1 NegativeBinomial | Source/Panjer.cs |
Related: