@actuarial-ts/core 0.1.0

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.
Files changed (120) hide show
  1. package/LICENSE +202 -0
  2. package/NOTICE +5 -0
  3. package/README.md +133 -0
  4. package/dist/benktander.d.ts +45 -0
  5. package/dist/benktander.d.ts.map +1 -0
  6. package/dist/benktander.js +38 -0
  7. package/dist/benktander.js.map +1 -0
  8. package/dist/berquist.d.ts +14 -0
  9. package/dist/berquist.d.ts.map +1 -0
  10. package/dist/berquist.js +292 -0
  11. package/dist/berquist.js.map +1 -0
  12. package/dist/bf.d.ts +23 -0
  13. package/dist/bf.d.ts.map +1 -0
  14. package/dist/bf.js +83 -0
  15. package/dist/bf.js.map +1 -0
  16. package/dist/capping.d.ts +96 -0
  17. package/dist/capping.d.ts.map +1 -0
  18. package/dist/capping.js +207 -0
  19. package/dist/capping.js.map +1 -0
  20. package/dist/caseOutstanding.d.ts +80 -0
  21. package/dist/caseOutstanding.d.ts.map +1 -0
  22. package/dist/caseOutstanding.js +147 -0
  23. package/dist/caseOutstanding.js.map +1 -0
  24. package/dist/chainladder.d.ts +15 -0
  25. package/dist/chainladder.d.ts.map +1 -0
  26. package/dist/chainladder.js +81 -0
  27. package/dist/chainladder.js.map +1 -0
  28. package/dist/clark.d.ts +91 -0
  29. package/dist/clark.d.ts.map +1 -0
  30. package/dist/clark.js +490 -0
  31. package/dist/clark.js.map +1 -0
  32. package/dist/diagnostics.d.ts +80 -0
  33. package/dist/diagnostics.d.ts.map +1 -0
  34. package/dist/diagnostics.js +359 -0
  35. package/dist/diagnostics.js.map +1 -0
  36. package/dist/discounting.d.ts +166 -0
  37. package/dist/discounting.d.ts.map +1 -0
  38. package/dist/discounting.js +210 -0
  39. package/dist/discounting.js.map +1 -0
  40. package/dist/elrMethods.d.ts +104 -0
  41. package/dist/elrMethods.d.ts.map +1 -0
  42. package/dist/elrMethods.js +151 -0
  43. package/dist/elrMethods.js.map +1 -0
  44. package/dist/factors.d.ts +19 -0
  45. package/dist/factors.d.ts.map +1 -0
  46. package/dist/factors.js +136 -0
  47. package/dist/factors.js.map +1 -0
  48. package/dist/fisherLange.d.ts +92 -0
  49. package/dist/fisherLange.d.ts.map +1 -0
  50. package/dist/fisherLange.js +224 -0
  51. package/dist/fisherLange.js.map +1 -0
  52. package/dist/freqSev.d.ts +57 -0
  53. package/dist/freqSev.d.ts.map +1 -0
  54. package/dist/freqSev.js +82 -0
  55. package/dist/freqSev.js.map +1 -0
  56. package/dist/ilf.d.ts +111 -0
  57. package/dist/ilf.d.ts.map +1 -0
  58. package/dist/ilf.js +449 -0
  59. package/dist/ilf.js.map +1 -0
  60. package/dist/index.d.ts +29 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +29 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/mack.d.ts +55 -0
  65. package/dist/mack.d.ts.map +1 -0
  66. package/dist/mack.js +244 -0
  67. package/dist/mack.js.map +1 -0
  68. package/dist/merzWuthrich.d.ts +29 -0
  69. package/dist/merzWuthrich.d.ts.map +1 -0
  70. package/dist/merzWuthrich.js +132 -0
  71. package/dist/merzWuthrich.js.map +1 -0
  72. package/dist/munichChainLadder.d.ts +101 -0
  73. package/dist/munichChainLadder.d.ts.map +1 -0
  74. package/dist/munichChainLadder.js +254 -0
  75. package/dist/munichChainLadder.js.map +1 -0
  76. package/dist/odpBootstrap.d.ts +65 -0
  77. package/dist/odpBootstrap.d.ts.map +1 -0
  78. package/dist/odpBootstrap.js +243 -0
  79. package/dist/odpBootstrap.js.map +1 -0
  80. package/dist/onlevel.d.ts +39 -0
  81. package/dist/onlevel.d.ts.map +1 -0
  82. package/dist/onlevel.js +104 -0
  83. package/dist/onlevel.js.map +1 -0
  84. package/dist/salvageSubro.d.ts +82 -0
  85. package/dist/salvageSubro.d.ts.map +1 -0
  86. package/dist/salvageSubro.js +100 -0
  87. package/dist/salvageSubro.js.map +1 -0
  88. package/dist/stochastic.d.ts +56 -0
  89. package/dist/stochastic.d.ts.map +1 -0
  90. package/dist/stochastic.js +99 -0
  91. package/dist/stochastic.js.map +1 -0
  92. package/dist/tail.d.ts +18 -0
  93. package/dist/tail.d.ts.map +1 -0
  94. package/dist/tail.js +121 -0
  95. package/dist/tail.js.map +1 -0
  96. package/dist/trend.d.ts +46 -0
  97. package/dist/trend.d.ts.map +1 -0
  98. package/dist/trend.js +99 -0
  99. package/dist/trend.js.map +1 -0
  100. package/dist/triangle.d.ts +19 -0
  101. package/dist/triangle.d.ts.map +1 -0
  102. package/dist/triangle.js +163 -0
  103. package/dist/triangle.js.map +1 -0
  104. package/dist/triangleAlgebra.d.ts +10 -0
  105. package/dist/triangleAlgebra.d.ts.map +1 -0
  106. package/dist/triangleAlgebra.js +97 -0
  107. package/dist/triangleAlgebra.js.map +1 -0
  108. package/dist/types.d.ts +280 -0
  109. package/dist/types.d.ts.map +1 -0
  110. package/dist/types.js +79 -0
  111. package/dist/types.js.map +1 -0
  112. package/dist/ulae.d.ts +172 -0
  113. package/dist/ulae.d.ts.map +1 -0
  114. package/dist/ulae.js +164 -0
  115. package/dist/ulae.js.map +1 -0
  116. package/dist/util.d.ts +24 -0
  117. package/dist/util.d.ts.map +1 -0
  118. package/dist/util.js +66 -0
  119. package/dist/util.js.map +1 -0
  120. package/package.json +66 -0
@@ -0,0 +1,207 @@
1
+ import { ReservingError } from "./types.js";
2
+ import { isNum } from "./util.js";
3
+ function accidentYear(snap) {
4
+ return Number(snap.accidentDate.slice(0, 4));
5
+ }
6
+ /**
7
+ * The default base year for a cap index: the latest accident year with a
8
+ * snapshot evaluated on or before asOfDate. capClaims and
9
+ * claimSizeDiagnostics MUST resolve this identically, or the applied caps
10
+ * diverge from what the exhibits report; services should resolve it once via
11
+ * this helper and pass baseYear explicitly to both.
12
+ */
13
+ export function latestAccidentYear(claims, asOfDate) {
14
+ const asOf = asOfDate ?? "9999-12-31";
15
+ let year = -Infinity;
16
+ for (const snap of claims) {
17
+ if (snap.evaluationDate > asOf)
18
+ continue;
19
+ year = Math.max(year, accidentYear(snap));
20
+ }
21
+ if (!Number.isFinite(year)) {
22
+ throw new ReservingError("NO_CLAIMS", "No claim snapshots on or before the analysis date; cannot resolve a cap base year");
23
+ }
24
+ return year;
25
+ }
26
+ /** The effective cap for one accident year under the index. */
27
+ export function effectiveCap(options, year) {
28
+ return options.cap * Math.pow(1 + options.indexRate, year - options.baseYear);
29
+ }
30
+ function resolveOptions(claims, options) {
31
+ if (!isNum(options.cap) || options.cap <= 0) {
32
+ throw new ReservingError("BAD_CAP", "The per-occurrence cap must be a positive number");
33
+ }
34
+ const indexRate = options.indexRate ?? 0;
35
+ if (!isNum(indexRate) || indexRate <= -1) {
36
+ throw new ReservingError("BAD_CAP", "The cap index rate must be a number greater than -100%");
37
+ }
38
+ let baseYear = options.baseYear ?? null;
39
+ if (baseYear === null) {
40
+ baseYear = -Infinity;
41
+ for (const snap of claims)
42
+ baseYear = Math.max(baseYear, accidentYear(snap));
43
+ }
44
+ if (!isNum(baseYear) || !Number.isFinite(baseYear)) {
45
+ throw new ReservingError("BAD_CAP", "Could not resolve a base year for the cap index");
46
+ }
47
+ return { cap: options.cap, indexRate, baseYear };
48
+ }
49
+ /**
50
+ * Caps every snapshot at the (indexed) per-occurrence limit for its accident
51
+ * year: capped incurred = min(reported incurred, cap_y); capped paid =
52
+ * min(paid, cap_y); capped case = capped incurred - capped paid (never
53
+ * negative by construction). Claim counts and statuses are untouched - the
54
+ * cap limits dollars, not claims.
55
+ */
56
+ export function capClaims(claims, options) {
57
+ const resolved = resolveOptions(claims, options);
58
+ return claims.map((snap) => {
59
+ const capY = effectiveCap(resolved, accidentYear(snap));
60
+ const incurred = snap.paidToDate + snap.caseReserve;
61
+ const cappedIncurred = Math.min(incurred, capY);
62
+ const cappedPaid = Math.min(snap.paidToDate, capY);
63
+ if (cappedIncurred === incurred && cappedPaid === snap.paidToDate)
64
+ return snap;
65
+ return {
66
+ ...snap,
67
+ paidToDate: cappedPaid,
68
+ caseReserve: cappedIncurred - cappedPaid,
69
+ };
70
+ });
71
+ }
72
+ const PERCENTILES = [0.5, 0.75, 0.9, 0.95, 0.99];
73
+ // Crude empirical convention (ceil-rank, no interpolation) is deliberate:
74
+ // candidate caps get rounded to 1-2 significant digits right after, so the
75
+ // interpolated refinement in stochastic.ts's percentileOfSorted buys nothing.
76
+ function percentile(sorted, p) {
77
+ if (sorted.length === 0)
78
+ return 0;
79
+ const idx = Math.min(sorted.length - 1, Math.max(0, Math.ceil(p * sorted.length) - 1));
80
+ return sorted[idx];
81
+ }
82
+ /** Round to 1-2 significant digits for readable candidate caps. */
83
+ function roundCandidate(value) {
84
+ if (value <= 0)
85
+ return 0;
86
+ const magnitude = Math.pow(10, Math.floor(Math.log10(value)));
87
+ const scaled = value / magnitude;
88
+ const nice = scaled < 1.5 ? 1 : scaled < 3.5 ? 2.5 : scaled < 7.5 ? 5 : 10;
89
+ return nice * magnitude;
90
+ }
91
+ /**
92
+ * Latest-evaluation claim-size distribution by accident year plus pierce and
93
+ * excess-share statistics for a set of candidate caps (indexed to each year).
94
+ */
95
+ export function claimSizeDiagnostics(claims, options = {}) {
96
+ if (claims.length === 0) {
97
+ throw new ReservingError("NO_CLAIMS", "Cannot analyze claim sizes with no claims");
98
+ }
99
+ const asOf = options.asOfDate ?? "9999-12-31";
100
+ // Latest evaluation per claim on or before the analysis date.
101
+ const latest = new Map();
102
+ for (const snap of claims) {
103
+ if (snap.evaluationDate > asOf)
104
+ continue;
105
+ const prev = latest.get(snap.claimId);
106
+ if (!prev || snap.evaluationDate > prev.evaluationDate)
107
+ latest.set(snap.claimId, snap);
108
+ }
109
+ const byYear = new Map();
110
+ for (const snap of latest.values()) {
111
+ const year = accidentYear(snap);
112
+ const incurred = snap.paidToDate + snap.caseReserve;
113
+ let arr = byYear.get(year);
114
+ if (!arr)
115
+ byYear.set(year, (arr = []));
116
+ arr.push(incurred);
117
+ }
118
+ const years = [...byYear.keys()].sort((a, b) => a - b);
119
+ const indexRate = options.indexRate ?? 0;
120
+ const baseYear = options.baseYear ?? years[years.length - 1];
121
+ const yearRows = years.map((year) => {
122
+ const sorted = [...byYear.get(year)].sort((a, b) => a - b);
123
+ return {
124
+ year,
125
+ claimCount: sorted.length,
126
+ totalIncurred: sorted.reduce((a, v) => a + v, 0),
127
+ maxClaim: sorted[sorted.length - 1] ?? 0,
128
+ percentiles: PERCENTILES.map((p) => ({ p, value: percentile(sorted, p) })),
129
+ };
130
+ });
131
+ // Candidate caps: provided, or derived from the pooled distribution so the
132
+ // menu spans "caps almost nothing" to "caps only the extreme tail".
133
+ // Candidates are STATED at the base-year cost level, so each claim's
134
+ // incurred is deflated to that level before taking percentiles - otherwise
135
+ // a nominal anchor re-indexed forward overshoots the whole distribution
136
+ // and every derived candidate reads near-zero pierce.
137
+ let candidateCaps = options.candidateCaps;
138
+ if (!candidateCaps || candidateCaps.length === 0) {
139
+ const pooled = [...latest.values()]
140
+ .map((s) => {
141
+ const incurred = s.paidToDate + s.caseReserve;
142
+ return incurred / Math.pow(1 + indexRate, accidentYear(s) - baseYear);
143
+ })
144
+ .filter((v) => v > 0)
145
+ .sort((a, b) => a - b);
146
+ const anchors = [0.75, 0.9, 0.95, 0.99].map((p) => percentile(pooled, p));
147
+ const uniq = new Set();
148
+ for (const a of anchors) {
149
+ const rounded = roundCandidate(a);
150
+ if (rounded > 0)
151
+ uniq.add(rounded);
152
+ }
153
+ for (const extra of options.extraCaps ?? []) {
154
+ if (extra > 0)
155
+ uniq.add(extra);
156
+ }
157
+ candidateCaps = [...uniq].sort((a, b) => a - b);
158
+ }
159
+ const resolved = { cap: 1, indexRate, baseYear };
160
+ const candidates = candidateCaps.map((cap) => {
161
+ let totalPierce = 0;
162
+ let totalClaims = 0;
163
+ let totalExcess = 0;
164
+ let totalIncurred = 0;
165
+ const cells = years.map((year) => {
166
+ const capY = cap * Math.pow(1 + resolved.indexRate, year - resolved.baseYear);
167
+ const values = byYear.get(year);
168
+ let pierce = 0;
169
+ let excess = 0;
170
+ let incurred = 0;
171
+ for (const v of values) {
172
+ incurred += v;
173
+ if (v > capY) {
174
+ pierce++;
175
+ excess += v - capY;
176
+ }
177
+ }
178
+ totalPierce += pierce;
179
+ totalClaims += values.length;
180
+ totalExcess += excess;
181
+ totalIncurred += incurred;
182
+ return {
183
+ year,
184
+ effectiveCap: capY,
185
+ pierceCount: pierce,
186
+ pierceShare: values.length > 0 ? pierce / values.length : 0,
187
+ excessShare: incurred > 0 ? excess / incurred : 0,
188
+ };
189
+ });
190
+ return {
191
+ cap,
192
+ byYear: cells,
193
+ totalPierceCount: totalPierce,
194
+ totalPierceShare: totalClaims > 0 ? totalPierce / totalClaims : 0,
195
+ totalExcessShare: totalIncurred > 0 ? totalExcess / totalIncurred : 0,
196
+ };
197
+ });
198
+ return {
199
+ years: yearRows,
200
+ candidates,
201
+ baseYear,
202
+ indexRate,
203
+ nonZeroClaimCount: [...latest.values()].filter((s) => s.paidToDate + s.caseReserve > 0)
204
+ .length,
205
+ };
206
+ }
207
+ //# sourceMappingURL=capping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capping.js","sourceRoot":"","sources":["../src/capping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,80 @@
1
+ import type { Triangle } from "./types.js";
2
+ /**
3
+ * Case-outstanding development technique (Friedland, "Estimating Unpaid
4
+ * Claims Using Basic Techniques", ch. 12): for books where the case reserves
5
+ * are the only reliable triangle (classic self-insured situation), unpaid
6
+ * claims are projected from the run-off of the case outstanding itself.
7
+ *
8
+ * Ground truth:
9
+ * - Two historical ratio triangles drive the review:
10
+ * caseRatios[i][j] = case[i][j+1] / case[i][j] (case run-off), and
11
+ * paidOnPriorCase[i][j] = (paid[i][j+1] - paid[i][j]) / case[i][j]
12
+ * (incremental paid in the interval as a ratio of the case reserve held at
13
+ * its start - "paid on prior case").
14
+ * - The CALLER selects both patterns (this module never picks factors). The
15
+ * projection walks each origin's latest case forward: payments in the next
16
+ * interval = case x paidOnCaseSelection, next case = case x caseSelection.
17
+ * - Case remaining at the last development age pays out at `tailPaidOnCase`
18
+ * times its face value (default 1: paid in full, the conventional
19
+ * assumption; a warning notes when the default was relied on).
20
+ * - unpaid = the sum of the projected future payments including the tail
21
+ * payout - the reserve IS the projected paid stream, an identity the
22
+ * self-consistency test enforces.
23
+ *
24
+ * Selection coercions (loud, never silent):
25
+ * - missing case selection -> 1.000 (case carried forward), warned;
26
+ * - negative case selection -> 0.000 (case cannot go negative), warned;
27
+ * - missing paid-on-case selection -> 0.000 (no payment projected), warned;
28
+ * - negative paid-on-case selection -> kept (projects net recoveries), warned;
29
+ * - ALL selections missing across both arrays -> NO_SELECTIONS.
30
+ */
31
+ export interface CaseOutstandingOptions {
32
+ /** Selected case run-off ratios (next case / current case), one per interval. */
33
+ caseSelections: (number | null)[];
34
+ /** Selected incremental-paid-to-prior-case ratios, one per interval. */
35
+ paidOnCaseSelections: (number | null)[];
36
+ /**
37
+ * Payments after the last age as a ratio of the case remaining there.
38
+ * Default 1 (remaining case pays out in full).
39
+ */
40
+ tailPaidOnCase?: number;
41
+ }
42
+ export interface CaseOutstandingRow {
43
+ origin: string;
44
+ /** Age (months) of the latest cell observed in BOTH triangles. */
45
+ latestAge: number;
46
+ paidToDate: number;
47
+ /** Case outstanding at the latest age (the projection's seed). */
48
+ caseOutstanding: number;
49
+ /**
50
+ * Projected future incremental paid: futurePaid[m] pays in the interval
51
+ * ending at the (m+1)-th age after latestAge; the LAST element is the tail
52
+ * payout of the case remaining at the final development age.
53
+ */
54
+ futurePaid: number[];
55
+ /** Projected case outstanding at each later age (parallel run-off path). */
56
+ projectedCase: number[];
57
+ /** Sum of futurePaid (the reserve). */
58
+ unpaid: number;
59
+ /** paidToDate + unpaid. */
60
+ ultimate: number;
61
+ }
62
+ export interface CaseOutstandingResult {
63
+ method: "caseOutstanding";
64
+ /** Historical case run-off ratios, [origin][interval]; null where not computable. */
65
+ caseRatios: (number | null)[][];
66
+ /** Historical incremental paid / prior case, [origin][interval]. */
67
+ paidOnPriorCase: (number | null)[][];
68
+ /** The tail payout ratio the projection used. */
69
+ tailPaidOnCase: number;
70
+ rows: CaseOutstandingRow[];
71
+ totals: {
72
+ paidToDate: number;
73
+ caseOutstanding: number;
74
+ unpaid: number;
75
+ ultimate: number;
76
+ };
77
+ warnings: string[];
78
+ }
79
+ export declare function runCaseOutstanding(paid: Triangle, caseOutstanding: Triangle, options: CaseOutstandingOptions): CaseOutstandingResult;
80
+ //# sourceMappingURL=caseOutstanding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caseOutstanding.d.ts","sourceRoot":"","sources":["../src/caseOutstanding.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,MAAM,WAAW,sBAAsB;IACrC,iFAAiF;IACjF,cAAc,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IAClC,wEAAwE;IACxE,oBAAoB,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;IACxC;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,kEAAkE;IAClE,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,kEAAkE;IAClE,eAAe,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4EAA4E;IAC5E,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,qFAAqF;IACrF,UAAU,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IAChC,oEAAoE;IACpE,eAAe,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,CAAC;IACrC,iDAAiD;IACjD,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,MAAM,EAAE;QAAE,UAAU,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC1F,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAcD,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,QAAQ,EACd,eAAe,EAAE,QAAQ,EACzB,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CA6KvB"}
@@ -0,0 +1,147 @@
1
+ import { ReservingError } from "./types.js";
2
+ import { isNum, lastJointObservedIndex, lastObservedIndex, safeRatio } from "./util.js";
3
+ function assertSameShape(a, b) {
4
+ const sameOrigins = a.origins.length === b.origins.length && a.origins.every((o, i) => o === b.origins[i]);
5
+ const sameAges = a.ages.length === b.ages.length && a.ages.every((v, j) => v === b.ages[j]);
6
+ if (!sameOrigins || !sameAges) {
7
+ throw new ReservingError("SHAPE", "The paid and case-outstanding triangles must share identical origins and development ages");
8
+ }
9
+ }
10
+ export function runCaseOutstanding(paid, caseOutstanding, options) {
11
+ assertSameShape(paid, caseOutstanding);
12
+ const nOrigins = paid.origins.length;
13
+ const nAges = paid.ages.length;
14
+ const nIntervals = nAges - 1;
15
+ for (const [name, selections] of [
16
+ ["caseSelections", options.caseSelections],
17
+ ["paidOnCaseSelections", options.paidOnCaseSelections],
18
+ ]) {
19
+ if (selections.length !== nIntervals) {
20
+ throw new ReservingError("SELECTION_SHAPE", `Expected ${nIntervals} ${name} (one per development interval), got ${selections.length}`);
21
+ }
22
+ }
23
+ const tailPaidOnCase = options.tailPaidOnCase ?? 1;
24
+ if (!isNum(tailPaidOnCase) || tailPaidOnCase < 0) {
25
+ throw new ReservingError("BAD_TAIL", `tailPaidOnCase must be a finite, non-negative ratio, got ${options.tailPaidOnCase}`);
26
+ }
27
+ const anySelected = options.caseSelections.some((s) => isNum(s)) ||
28
+ options.paidOnCaseSelections.some((s) => isNum(s));
29
+ if (nIntervals > 0 && !anySelected) {
30
+ throw new ReservingError("NO_SELECTIONS", "No case run-off or paid-on-case ratios are selected for any interval; select patterns before running the analysis");
31
+ }
32
+ const warnings = [];
33
+ // Effective selections with the documented coercions, warned per interval.
34
+ const effectiveCase = options.caseSelections.map((s, j) => {
35
+ const label = `${paid.ages[j]}-${paid.ages[j + 1]} months`;
36
+ if (!isNum(s)) {
37
+ warnings.push(`Missing case run-off selection for ${label}; treated as 1.000 (case carried forward)`);
38
+ return 1;
39
+ }
40
+ if (s < 0) {
41
+ warnings.push(`Negative case run-off selection for ${label} would project negative case; treated as 0.000`);
42
+ return 0;
43
+ }
44
+ return s;
45
+ });
46
+ const effectivePaid = options.paidOnCaseSelections.map((s, j) => {
47
+ const label = `${paid.ages[j]}-${paid.ages[j + 1]} months`;
48
+ if (!isNum(s)) {
49
+ warnings.push(`Missing paid-on-case selection for ${label}; treated as 0.000 (no payment projected)`);
50
+ return 0;
51
+ }
52
+ if (s < 0) {
53
+ warnings.push(`Negative paid-on-case selection for ${label} projects net recoveries; kept as given`);
54
+ }
55
+ return s;
56
+ });
57
+ // Historical ratio triangles (null-safe: prior case must be positive, and
58
+ // an incremental paid needs both cumulative cells observed).
59
+ const caseRatios = [];
60
+ const paidOnPriorCase = [];
61
+ for (let i = 0; i < nOrigins; i++) {
62
+ const cRow = [];
63
+ const pRow = [];
64
+ for (let j = 0; j < nIntervals; j++) {
65
+ const casePrior = caseOutstanding.values[i][j] ?? null;
66
+ const caseNext = caseOutstanding.values[i][j + 1] ?? null;
67
+ const paidPrior = paid.values[i][j] ?? null;
68
+ const paidNext = paid.values[i][j + 1] ?? null;
69
+ cRow.push(safeRatio(caseNext, casePrior));
70
+ pRow.push(isNum(paidPrior) && isNum(paidNext)
71
+ ? safeRatio(paidNext - paidPrior, casePrior)
72
+ : null);
73
+ }
74
+ caseRatios.push(cRow);
75
+ paidOnPriorCase.push(pRow);
76
+ }
77
+ const rows = [];
78
+ let tailDefaultRelevant = false;
79
+ for (let i = 0; i < nOrigins; i++) {
80
+ const paidRow = paid.values[i];
81
+ const caseRow = caseOutstanding.values[i];
82
+ const lastPaid = lastObservedIndex(paidRow);
83
+ const lastCase = lastObservedIndex(caseRow);
84
+ const k = lastJointObservedIndex(paidRow, caseRow);
85
+ if (k < 0) {
86
+ warnings.push(`Origin ${paid.origins[i]} has no age with both paid and case outstanding observed; excluded from results`);
87
+ continue;
88
+ }
89
+ if (lastPaid !== lastCase) {
90
+ warnings.push(`Origin ${paid.origins[i]}: paid and case diagonals end at different ages (${paid.ages[lastPaid]} vs ${paid.ages[lastCase]}); projected from the latest jointly observed age ${paid.ages[k]}`);
91
+ }
92
+ const paidToDate = paidRow[k];
93
+ const seedCase = caseRow[k];
94
+ if (seedCase < 0) {
95
+ warnings.push(`Origin ${paid.origins[i]}: case outstanding at the seed age is negative (${seedCase}); the projection carries the sign through, so review whether net recoveries are genuinely expected`);
96
+ }
97
+ const futurePaid = [];
98
+ const projectedCase = [];
99
+ let current = seedCase;
100
+ let warnedNegativeProjection = seedCase < 0;
101
+ for (let j = k; j < nIntervals; j++) {
102
+ futurePaid.push(current * effectivePaid[j]);
103
+ current *= effectiveCase[j];
104
+ projectedCase.push(current);
105
+ if (current < 0 && !warnedNegativeProjection) {
106
+ warnedNegativeProjection = true;
107
+ warnings.push(`Origin ${paid.origins[i]}: projected case outstanding turns negative (${current}) at age ${paid.ages[j + 1] ?? "tail"}; downstream reserves inherit the sign`);
108
+ }
109
+ }
110
+ if (current !== 0 && options.tailPaidOnCase === undefined)
111
+ tailDefaultRelevant = true;
112
+ futurePaid.push(current * tailPaidOnCase);
113
+ const unpaid = futurePaid.reduce((a, v) => a + v, 0);
114
+ rows.push({
115
+ origin: paid.origins[i],
116
+ latestAge: paid.ages[k],
117
+ paidToDate,
118
+ caseOutstanding: seedCase,
119
+ futurePaid,
120
+ projectedCase,
121
+ unpaid,
122
+ ultimate: paidToDate + unpaid,
123
+ });
124
+ }
125
+ if (rows.length === 0) {
126
+ throw new ReservingError("NO_DATA", "No origin period has both paid and case outstanding observed at any age");
127
+ }
128
+ if (tailDefaultRelevant) {
129
+ warnings.push(`Case outstanding remaining at ${paid.ages[nAges - 1]} months is assumed paid in full (tailPaidOnCase defaulted to 1); supply tailPaidOnCase to select otherwise`);
130
+ }
131
+ const totals = rows.reduce((acc, r) => ({
132
+ paidToDate: acc.paidToDate + r.paidToDate,
133
+ caseOutstanding: acc.caseOutstanding + r.caseOutstanding,
134
+ unpaid: acc.unpaid + r.unpaid,
135
+ ultimate: acc.ultimate + r.ultimate,
136
+ }), { paidToDate: 0, caseOutstanding: 0, unpaid: 0, ultimate: 0 });
137
+ return {
138
+ method: "caseOutstanding",
139
+ caseRatios,
140
+ paidOnPriorCase,
141
+ tailPaidOnCase,
142
+ rows,
143
+ totals,
144
+ warnings,
145
+ };
146
+ }
147
+ //# sourceMappingURL=caseOutstanding.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"caseOutstanding.js","sourceRoot":"","sources":["../src/caseOutstanding.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ import type { ChainLadderResult, LdfSelections, Triangle } from "./types.js";
2
+ /**
3
+ * Chain Ladder (development) method.
4
+ *
5
+ * Ground truth:
6
+ * - CDFs are computed right to left: CDF at the last observed age = tail
7
+ * factor; CDF_j = LDF_j x CDF_{j+1}.
8
+ * - Percent developed = 1 / CDF.
9
+ * - Ultimate = latest diagonal value x CDF at its age.
10
+ * - IBNR (incurred basis) / unpaid (paid basis) = ultimate - current.
11
+ * - Missing LDF selections: warn loudly and treat as 1.0; refuse to run when
12
+ * ALL selections are missing.
13
+ */
14
+ export declare function runChainLadder(tri: Triangle, selections: LdfSelections): ChainLadderResult;
15
+ //# sourceMappingURL=chainladder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chainladder.d.ts","sourceRoot":"","sources":["../src/chainladder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAkB,aAAa,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAI7F;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,GAAG,iBAAiB,CAoF1F"}
@@ -0,0 +1,81 @@
1
+ import { ReservingError } from "./types.js";
2
+ import { isNum, lastObservedIndex } from "./util.js";
3
+ /**
4
+ * Chain Ladder (development) method.
5
+ *
6
+ * Ground truth:
7
+ * - CDFs are computed right to left: CDF at the last observed age = tail
8
+ * factor; CDF_j = LDF_j x CDF_{j+1}.
9
+ * - Percent developed = 1 / CDF.
10
+ * - Ultimate = latest diagonal value x CDF at its age.
11
+ * - IBNR (incurred basis) / unpaid (paid basis) = ultimate - current.
12
+ * - Missing LDF selections: warn loudly and treat as 1.0; refuse to run when
13
+ * ALL selections are missing.
14
+ */
15
+ export function runChainLadder(tri, selections) {
16
+ const nAges = tri.ages.length;
17
+ const nLdfs = nAges - 1;
18
+ if (selections.selected.length !== nLdfs) {
19
+ throw new ReservingError("SELECTION_SHAPE", `Expected ${nLdfs} LDF selections (one per development interval), got ${selections.selected.length}`);
20
+ }
21
+ if (!isNum(selections.tailFactor) || selections.tailFactor <= 0) {
22
+ throw new ReservingError("BAD_TAIL", "Tail factor must be a positive number");
23
+ }
24
+ const warnings = [];
25
+ const anySelected = selections.selected.some((s) => isNum(s));
26
+ if (nLdfs > 0 && !anySelected) {
27
+ throw new ReservingError("NO_SELECTIONS", "No LDFs are selected for any development interval; select factors before running the analysis");
28
+ }
29
+ const effective = selections.selected.map((s, j) => {
30
+ if (isNum(s)) {
31
+ if (s <= 0) {
32
+ warnings.push(`Selected LDF for ${tri.ages[j]}-${tri.ages[j + 1]} months is not positive; treated as 1.000`);
33
+ return 1;
34
+ }
35
+ return s;
36
+ }
37
+ warnings.push(`Missing LDF selection for ${tri.ages[j]}-${tri.ages[j + 1]} months; treated as 1.000`);
38
+ return 1;
39
+ });
40
+ // Right-to-left cumulative development factors; cdfs[j] develops age[j] to ultimate.
41
+ const cdfs = new Array(nAges).fill(selections.tailFactor);
42
+ for (let j = nAges - 2; j >= 0; j--) {
43
+ cdfs[j] = effective[j] * cdfs[j + 1];
44
+ }
45
+ const percentDeveloped = cdfs.map((c) => 1 / c);
46
+ const rows = [];
47
+ for (let i = 0; i < tri.origins.length; i++) {
48
+ const latestIdx = lastObservedIndex(tri.values[i]);
49
+ if (latestIdx < 0) {
50
+ warnings.push(`Origin ${tri.origins[i]} has no observed values; excluded from results`);
51
+ continue;
52
+ }
53
+ const latestValue = tri.values[i][latestIdx];
54
+ const cdf = cdfs[latestIdx];
55
+ const ultimate = latestValue * cdf;
56
+ rows.push({
57
+ origin: tri.origins[i],
58
+ latestAge: tri.ages[latestIdx],
59
+ latestValue,
60
+ cdf,
61
+ percentDeveloped: 1 / cdf,
62
+ ultimate,
63
+ unpaid: ultimate - latestValue,
64
+ });
65
+ }
66
+ const totals = rows.reduce((acc, r) => ({
67
+ latest: acc.latest + r.latestValue,
68
+ ultimate: acc.ultimate + r.ultimate,
69
+ unpaid: acc.unpaid + r.unpaid,
70
+ }), { latest: 0, ultimate: 0, unpaid: 0 });
71
+ return {
72
+ method: "chainLadder",
73
+ basis: tri.kind,
74
+ cdfs,
75
+ percentDeveloped,
76
+ rows,
77
+ totals,
78
+ warnings,
79
+ };
80
+ }
81
+ //# sourceMappingURL=chainladder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chainladder.js","sourceRoot":"","sources":["../src/chainladder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAErD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,cAAc,CAAC,GAAa,EAAE,UAAyB;IACrE,MAAM,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,MAAM,CAAC;IAC9B,MAAM,KAAK,GAAG,KAAK,GAAG,CAAC,CAAC;IACxB,IAAI,UAAU,CAAC,QAAQ,CAAC,MAAM,KAAK,KAAK,EAAE,CAAC;QACzC,MAAM,IAAI,cAAc,CACtB,iBAAiB,EACjB,YAAY,KAAK,uDAAuD,UAAU,CAAC,QAAQ,CAAC,MAAM,EAAE,CACrG,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,UAAU,CAAC,IAAI,UAAU,CAAC,UAAU,IAAI,CAAC,EAAE,CAAC;QAChE,MAAM,IAAI,cAAc,CAAC,UAAU,EAAE,uCAAuC,CAAC,CAAC;IAChF,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,MAAM,WAAW,GAAG,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAC9D,IAAI,KAAK,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;QAC9B,MAAM,IAAI,cAAc,CACtB,eAAe,EACf,+FAA+F,CAChG,CAAC;IACJ,CAAC;IAED,MAAM,SAAS,GAAa,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC3D,IAAI,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;YACb,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACX,QAAQ,CAAC,IAAI,CACX,oBAAoB,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,2CAA2C,CAC9F,CAAC;gBACF,OAAO,CAAC,CAAC;YACX,CAAC;YACD,OAAO,CAAC,CAAC;QACX,CAAC;QACD,QAAQ,CAAC,IAAI,CACX,6BAA6B,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,2BAA2B,CACvF,CAAC;QACF,OAAO,CAAC,CAAC;IACX,CAAC,CAAC,CAAC;IAEH,qFAAqF;IACrF,MAAM,IAAI,GAAa,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,CAAC;IACpE,KAAK,IAAI,CAAC,GAAG,KAAK,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;QACpC,IAAI,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAE,GAAG,IAAI,CAAC,CAAC,GAAG,CAAC,CAAE,CAAC;IACzC,CAAC;IACD,MAAM,gBAAgB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IAEhD,MAAM,IAAI,GAAqB,EAAE,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QAC5C,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,CAAC;QACpD,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;YAClB,QAAQ,CAAC,IAAI,CAAC,UAAU,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,gDAAgD,CAAC,CAAC;YACxF,SAAS;QACX,CAAC;QACD,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAE,CAAC,SAAS,CAAE,CAAC;QAC/C,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,WAAW,GAAG,GAAG,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC;YACR,MAAM,EAAE,GAAG,CAAC,OAAO,CAAC,CAAC,CAAE;YACvB,SAAS,EAAE,GAAG,CAAC,IAAI,CAAC,SAAS,CAAE;YAC/B,WAAW;YACX,GAAG;YACH,gBAAgB,EAAE,CAAC,GAAG,GAAG;YACzB,QAAQ;YACR,MAAM,EAAE,QAAQ,GAAG,WAAW;SAC/B,CAAC,CAAC;IACL,CAAC;IAED,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CACxB,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC;QACX,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,WAAW;QAClC,QAAQ,EAAE,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ;QACnC,MAAM,EAAE,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC,MAAM;KAC9B,CAAC,EACF,EAAE,MAAM,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CACtC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,aAAa;QACrB,KAAK,EAAE,GAAG,CAAC,IAAI;QACf,IAAI;QACJ,gBAAgB;QAChB,IAAI;QACJ,MAAM;QACN,QAAQ;KACT,CAAC;AACJ,CAAC"}