@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,80 @@
1
+ import type { CalendarYearDiagnostic, DiagnosticsResult, Triangle } from "./types.js";
2
+ /**
3
+ * Mack's calendar-year test: within each development column, factors above
4
+ * the column median are Large, below are Small (ties to the median drop out).
5
+ * Under the null of no diagonal effects, Large/Small mix randomly along each
6
+ * calendar diagonal; Z = sum over diagonals of min(#L, #S) has a known mean
7
+ * and variance. A total outside the 95% range flags calendar-year effects.
8
+ */
9
+ export declare function calendarYearTest(tri: Triangle): CalendarYearDiagnostic | null;
10
+ export interface DiagnosticsInput {
11
+ paid: Triangle;
12
+ incurred: Triangle;
13
+ openCounts: Triangle;
14
+ reportedCounts: Triangle;
15
+ closedCounts: Triangle;
16
+ }
17
+ export declare function runDiagnostics(input: DiagnosticsInput): DiagnosticsResult;
18
+ export interface FactorCorrelationColumn {
19
+ /** Development year k in Mack's 1-indexed notation (subsequent factors F_k vs preceding F_{k-1}). */
20
+ k: number;
21
+ /** Number of (preceding, subsequent) factor pairs used. */
22
+ pairs: number;
23
+ /** Spearman rank correlation T_k. */
24
+ statistic: number;
25
+ }
26
+ export interface FactorCorrelationResult {
27
+ columns: FactorCorrelationColumn[];
28
+ /** T: the (pairs-1)-weighted average of the T_k. E[T] = 0 under the null. */
29
+ statistic: number;
30
+ /** Var(T) = 1 / sum of weights. */
31
+ variance: number;
32
+ /** Half-width of the 50% interval: 0.67 x sqrt(Var(T)). */
33
+ bound50: number;
34
+ /** True when |T| exceeds the 50% bound (be reluctant with chain ladder). */
35
+ correlated: boolean;
36
+ warnings: string[];
37
+ }
38
+ /**
39
+ * Mack's Spearman test for correlation between subsequent development
40
+ * factors (Mack 1994, CAS Forum Spring 1994, Appendix G).
41
+ *
42
+ * For each development year k = 2..I-2: rank the subsequent factors F_k over
43
+ * the rows where the NEXT factor exists, rank the preceding factors F_{k-1}
44
+ * over those same rows (re-ranked after dropping the deeper rows), and take
45
+ * T_k = 1 - 6 sum(d^2) / (n(n^2-1)). Combine with weights (pairs - 1) —
46
+ * inverse to Var(T_k) = 1/(n_k - 1). Under the null E[T] = 0 and
47
+ * Var(T) = 1/sum(weights); the DELIBERATELY tight 50% interval
48
+ * (|T| <= 0.67 sqrt(Var)) screens for correlation in a substantial part of
49
+ * the triangle rather than formally testing at 5%.
50
+ *
51
+ * Returns null when no column pair has at least two factor pairs.
52
+ */
53
+ export declare function factorCorrelationTest(tri: Triangle): FactorCorrelationResult | null;
54
+ export interface MackResidualCell {
55
+ origin: string;
56
+ fromAge: number;
57
+ toAge: number;
58
+ /** 1-based calendar diagonal of the factor's numerator cell. */
59
+ calendar: number;
60
+ factor: number;
61
+ /** (F - f_k) sqrt(C_ik) / sigma_k; null where sigma^2 is not data-estimable or zero. */
62
+ residual: number | null;
63
+ }
64
+ export interface MackResidualsResult {
65
+ cells: MackResidualCell[];
66
+ warnings: string[];
67
+ }
68
+ /**
69
+ * Standardized Mack residuals r_ik = (F_ik - f_k) sqrt(C_ik) / sigma_k
70
+ * around the volume-weighted factors, with sigma^2_k estimated from the
71
+ * data (Mack 1994). Columns whose sigma^2 cannot be estimated from at least
72
+ * two factors — or where the factors show no dispersion at all — yield null
73
+ * residuals with a warning; extrapolated sigma^2 is deliberately NOT used
74
+ * here (a residual against an extrapolated scale is not a diagnostic).
75
+ *
76
+ * Structural property: within each column, sum_i residual_ik x sqrt(C_ik)
77
+ * = 0 exactly, because f_k is the volume-weighted average.
78
+ */
79
+ export declare function mackResiduals(tri: Triangle): MackResidualsResult;
80
+ //# sourceMappingURL=diagnostics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,sBAAsB,EAEtB,iBAAiB,EACjB,QAAQ,EACT,MAAM,YAAY,CAAC;AAqBpB;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,QAAQ,GAAG,sBAAsB,GAAG,IAAI,CA8E7E;AAsED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,EAAE,QAAQ,CAAC;IACrB,cAAc,EAAE,QAAQ,CAAC;IACzB,YAAY,EAAE,QAAQ,CAAC;CACxB;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,gBAAgB,GAAG,iBAAiB,CAgEzE;AAMD,MAAM,WAAW,uBAAuB;IACtC,qGAAqG;IACrG,CAAC,EAAE,MAAM,CAAC;IACV,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,6EAA6E;IAC7E,SAAS,EAAE,MAAM,CAAC;IAClB,mCAAmC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAmBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,QAAQ,GAAG,uBAAuB,GAAG,IAAI,CAyDnF;AAMD,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,wFAAwF;IACxF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,mBAAmB,CA+BhE"}
@@ -0,0 +1,359 @@
1
+ import { isNum, ols, safeRatio } from "./util.js";
2
+ import { mackEstimators } from "./mack.js";
3
+ /**
4
+ * Data diagnostics an actuary uses to see when the data violates method
5
+ * assumptions:
6
+ * - paid-to-incurred ratios (case adequacy drift distorts incurred methods)
7
+ * - average case reserves (severity/adequacy shifts)
8
+ * - closure rates (settlement-rate shifts distort paid development)
9
+ * - calendar-year effect detection (Mack's 1994 diagonal rank test)
10
+ */
11
+ function ratioGrid(numTri, denTri) {
12
+ return numTri.origins.map((_, i) => numTri.ages.map((_, j) => safeRatio(numTri.values[i]?.[j] ?? null, denTri.values[i]?.[j] ?? null)));
13
+ }
14
+ /**
15
+ * Mack's calendar-year test: within each development column, factors above
16
+ * the column median are Large, below are Small (ties to the median drop out).
17
+ * Under the null of no diagonal effects, Large/Small mix randomly along each
18
+ * calendar diagonal; Z = sum over diagonals of min(#L, #S) has a known mean
19
+ * and variance. A total outside the 95% range flags calendar-year effects.
20
+ */
21
+ export function calendarYearTest(tri) {
22
+ const nOrigins = tri.origins.length;
23
+ const nCols = tri.ages.length - 1;
24
+ if (nCols < 1 || nOrigins < 3)
25
+ return null;
26
+ const marks = tri.origins.map(() => new Array(nCols).fill(null));
27
+ for (let j = 0; j < nCols; j++) {
28
+ const entries = [];
29
+ for (let i = 0; i < nOrigins; i++) {
30
+ const f = safeRatio(tri.values[i]?.[j + 1] ?? null, tri.values[i]?.[j] ?? null);
31
+ if (isNum(f))
32
+ entries.push({ i, f });
33
+ }
34
+ if (entries.length < 2)
35
+ continue;
36
+ const sorted = entries.map((e) => e.f).sort((a, b) => a - b);
37
+ const mid = sorted.length % 2 === 1
38
+ ? sorted[(sorted.length - 1) / 2]
39
+ : (sorted[sorted.length / 2 - 1] + sorted[sorted.length / 2]) / 2;
40
+ for (const e of entries) {
41
+ if (e.f > mid)
42
+ marks[e.i][j] = "L";
43
+ else if (e.f < mid)
44
+ marks[e.i][j] = "S";
45
+ // equal to the median -> eliminated
46
+ }
47
+ }
48
+ // Group by calendar diagonal: factor cell (i, j) belongs to diagonal i + j + 1
49
+ // (the calendar period of the numerator age).
50
+ const byDiagonal = new Map();
51
+ for (let i = 0; i < nOrigins; i++) {
52
+ for (let j = 0; j < nCols; j++) {
53
+ const mark = marks[i][j];
54
+ if (!mark)
55
+ continue;
56
+ const d = i + j + 1;
57
+ const bucket = byDiagonal.get(d) ?? { L: 0, S: 0 };
58
+ bucket[mark]++;
59
+ byDiagonal.set(d, bucket);
60
+ }
61
+ }
62
+ const binom = (n, k) => {
63
+ if (k < 0 || k > n)
64
+ return 0;
65
+ let result = 1;
66
+ for (let x = 1; x <= k; x++)
67
+ result = (result * (n - x + 1)) / x;
68
+ return result;
69
+ };
70
+ const diagonals = [];
71
+ let totalZ = 0;
72
+ let expectedTotalZ = 0;
73
+ let varianceTotalZ = 0;
74
+ const sortedDiagonals = [...byDiagonal.entries()].sort((a, b) => a[0] - b[0]);
75
+ for (const [d, { L, S }] of sortedDiagonals) {
76
+ const n = L + S;
77
+ if (n < 1)
78
+ continue;
79
+ const z = Math.min(L, S);
80
+ const m = Math.floor((n - 1) / 2);
81
+ const c = binom(n - 1, m);
82
+ const expectedZ = n / 2 - (c * n) / 2 ** n;
83
+ const varianceZ = (n * (n - 1)) / 4 - (c * n * (n - 1)) / 2 ** n + expectedZ - expectedZ ** 2;
84
+ diagonals.push({ label: `diagonal ${d}`, countLarge: L, countSmall: S, z, expectedZ, varianceZ });
85
+ totalZ += z;
86
+ expectedTotalZ += expectedZ;
87
+ varianceTotalZ += varianceZ;
88
+ }
89
+ if (diagonals.length === 0)
90
+ return null;
91
+ const half = 1.96 * Math.sqrt(Math.max(0, varianceTotalZ));
92
+ const confidenceInterval = [expectedTotalZ - half, expectedTotalZ + half];
93
+ return {
94
+ diagonals,
95
+ totalZ,
96
+ expectedTotalZ,
97
+ varianceTotalZ,
98
+ significant: totalZ < confidenceInterval[0] || totalZ > confidenceInterval[1],
99
+ confidenceInterval,
100
+ };
101
+ }
102
+ /**
103
+ * Median relative shift of the most recent origin periods against the prior
104
+ * ones, per development column, restricted to columns that are still
105
+ * developing (median value below the maturity cap). This is how an actuary
106
+ * reads a closure-rate exhibit: a step change in the recent rows at immature
107
+ * ages, not a smooth trend across saturated columns.
108
+ */
109
+ function recentVsPriorShift(grid, options = {}) {
110
+ const recentN = options.recent ?? 3;
111
+ const maturityCap = options.maturityCap ?? Infinity;
112
+ if (grid.length === 0)
113
+ return null;
114
+ const nCols = grid[0].length;
115
+ const shifts = [];
116
+ for (let j = 0; j < nCols; j++) {
117
+ const values = [];
118
+ for (let i = 0; i < grid.length; i++) {
119
+ const v = grid[i][j] ?? null;
120
+ if (isNum(v))
121
+ values.push(v);
122
+ }
123
+ if (values.length < recentN + 2)
124
+ continue;
125
+ const sorted = [...values].sort((a, b) => a - b);
126
+ const median = sorted[Math.floor(sorted.length / 2)];
127
+ if (median >= maturityCap)
128
+ continue; // column is saturated; no signal left
129
+ const recent = values.slice(-recentN);
130
+ const prior = values.slice(0, values.length - recentN);
131
+ const avg = (xs) => xs.reduce((a, b) => a + b, 0) / xs.length;
132
+ const priorAvg = avg(prior);
133
+ if (priorAvg === 0)
134
+ continue;
135
+ shifts.push(avg(recent) / priorAvg - 1);
136
+ }
137
+ if (shifts.length === 0)
138
+ return null;
139
+ shifts.sort((a, b) => a - b);
140
+ const mid = Math.floor(shifts.length / 2);
141
+ return shifts.length % 2 === 1 ? shifts[mid] : (shifts[mid - 1] + shifts[mid]) / 2;
142
+ }
143
+ /**
144
+ * Median relative per-period trend of a metric across development columns,
145
+ * fitted down each column (same maturity across origin periods).
146
+ */
147
+ function medianColumnTrend(grid) {
148
+ if (grid.length === 0)
149
+ return null;
150
+ const nCols = grid[0].length;
151
+ const trends = [];
152
+ for (let j = 0; j < nCols; j++) {
153
+ const xs = [];
154
+ const ys = [];
155
+ for (let i = 0; i < grid.length; i++) {
156
+ const v = grid[i][j] ?? null;
157
+ if (isNum(v) && v > 0) {
158
+ xs.push(i);
159
+ ys.push(Math.log(v));
160
+ }
161
+ }
162
+ if (xs.length >= 3) {
163
+ const fit = ols(xs, ys);
164
+ if (fit)
165
+ trends.push(Math.exp(fit.slope) - 1);
166
+ }
167
+ }
168
+ if (trends.length === 0)
169
+ return null;
170
+ trends.sort((a, b) => a - b);
171
+ const mid = Math.floor(trends.length / 2);
172
+ return trends.length % 2 === 1 ? trends[mid] : (trends[mid - 1] + trends[mid]) / 2;
173
+ }
174
+ export function runDiagnostics(input) {
175
+ const { paid, incurred, openCounts, reportedCounts, closedCounts } = input;
176
+ const paidToIncurredRatios = ratioGrid(paid, incurred);
177
+ const caseGrid = paid.origins.map((_, i) => paid.ages.map((_, j) => {
178
+ const inc = incurred.values[i]?.[j] ?? null;
179
+ const pd = paid.values[i]?.[j] ?? null;
180
+ const open = openCounts.values[i]?.[j] ?? null;
181
+ const caseAmt = isNum(inc) && isNum(pd) ? inc - pd : null;
182
+ return safeRatio(caseAmt, open);
183
+ }));
184
+ const closureRates = ratioGrid(closedCounts, reportedCounts);
185
+ const cyTest = calendarYearTest(paid);
186
+ const findings = [];
187
+ const closureShift = recentVsPriorShift(closureRates, { recent: 3, maturityCap: 0.95 });
188
+ if (closureShift !== null && Math.abs(closureShift) > 0.05) {
189
+ findings.push({
190
+ severity: "warning",
191
+ code: "SETTLEMENT_RATE_SHIFT",
192
+ message: `Claim settlement is ${closureShift > 0 ? "speeding up" : "slowing down"}: at still-developing maturities, closure rates for the most recent origin periods run ${(Math.abs(closureShift) * 100).toFixed(0)}% ${closureShift > 0 ? "above" : "below"} the prior periods. Paid development factors are distorted; weigh the Berquist-Sherman settlement-rate adjustment.`,
193
+ });
194
+ }
195
+ const caseTrend = medianColumnTrend(caseGrid);
196
+ if (caseTrend !== null && Math.abs(caseTrend) > 0.08) {
197
+ findings.push({
198
+ severity: "warning",
199
+ code: "CASE_ADEQUACY_SHIFT",
200
+ message: `Average case reserves at the same maturity are trending ${caseTrend > 0 ? "up" : "down"} ${(caseTrend * 100).toFixed(1)}% per period, which suggests a change in case reserve adequacy (or severity). Incurred development is distorted; consider the Berquist-Sherman case-reserve adjustment and compare against a severity trend expectation.`,
201
+ });
202
+ }
203
+ const p2iTrend = medianColumnTrend(paidToIncurredRatios);
204
+ if (p2iTrend !== null && Math.abs(p2iTrend) > 0.03) {
205
+ findings.push({
206
+ severity: "info",
207
+ code: "PAID_TO_INCURRED_SHIFT",
208
+ message: `Paid-to-incurred ratios at the same maturity are drifting ${p2iTrend > 0 ? "up" : "down"} (median ${(p2iTrend * 100).toFixed(1)}% per period). Paid and incurred projections are likely to diverge; investigate before relying on either alone.`,
209
+ });
210
+ }
211
+ if (cyTest?.significant) {
212
+ findings.push({
213
+ severity: "warning",
214
+ code: "CALENDAR_YEAR_EFFECT",
215
+ message: `Mack's calendar-year test rejects the null of no diagonal effects (Z = ${cyTest.totalZ.toFixed(1)} vs expected ${cyTest.expectedTotalZ.toFixed(1)}, 95% range ${cyTest.confidenceInterval[0].toFixed(1)}-${cyTest.confidenceInterval[1].toFixed(1)}). Calendar-period influences (inflation spikes, process changes, reserve reviews) are present; development-based methods assume these away.`,
216
+ });
217
+ }
218
+ if (findings.length === 0) {
219
+ findings.push({
220
+ severity: "info",
221
+ code: "NO_MATERIAL_DISTORTIONS",
222
+ message: "No material settlement-rate, case-adequacy, or calendar-year distortions detected. Standard development assumptions look reasonable for this data.",
223
+ });
224
+ }
225
+ return {
226
+ paidToIncurredRatios,
227
+ averageCaseReserves: caseGrid,
228
+ closureRates,
229
+ calendarYearTest: cyTest,
230
+ findings,
231
+ };
232
+ }
233
+ /** Average ranks (ties averaged), 1-based. */
234
+ function averageRanks(values) {
235
+ const order = values
236
+ .map((v, idx) => ({ v, idx }))
237
+ .sort((a, b) => a.v - b.v);
238
+ const ranks = new Array(values.length).fill(0);
239
+ let pos = 0;
240
+ while (pos < order.length) {
241
+ let end = pos;
242
+ while (end + 1 < order.length && order[end + 1].v === order[pos].v)
243
+ end++;
244
+ const avg = (pos + end) / 2 + 1;
245
+ for (let x = pos; x <= end; x++)
246
+ ranks[order[x].idx] = avg;
247
+ pos = end + 1;
248
+ }
249
+ return ranks;
250
+ }
251
+ /**
252
+ * Mack's Spearman test for correlation between subsequent development
253
+ * factors (Mack 1994, CAS Forum Spring 1994, Appendix G).
254
+ *
255
+ * For each development year k = 2..I-2: rank the subsequent factors F_k over
256
+ * the rows where the NEXT factor exists, rank the preceding factors F_{k-1}
257
+ * over those same rows (re-ranked after dropping the deeper rows), and take
258
+ * T_k = 1 - 6 sum(d^2) / (n(n^2-1)). Combine with weights (pairs - 1) —
259
+ * inverse to Var(T_k) = 1/(n_k - 1). Under the null E[T] = 0 and
260
+ * Var(T) = 1/sum(weights); the DELIBERATELY tight 50% interval
261
+ * (|T| <= 0.67 sqrt(Var)) screens for correlation in a substantial part of
262
+ * the triangle rather than formally testing at 5%.
263
+ *
264
+ * Returns null when no column pair has at least two factor pairs.
265
+ */
266
+ export function factorCorrelationTest(tri) {
267
+ const n = tri.origins.length;
268
+ const nCols = tri.ages.length - 1;
269
+ const warnings = [];
270
+ // F[i][c] = C[i][c+1] / C[i][c] (0-indexed column c holds Mack's F_{c+1}).
271
+ const F = tri.origins.map((_, i) => Array.from({ length: nCols }, (_, c) => safeRatio(tri.values[i]?.[c + 1] ?? null, tri.values[i]?.[c] ?? null)));
272
+ const columns = [];
273
+ for (let k = 2; k <= Math.min(n - 2, nCols); k++) {
274
+ const cs = k - 1; // 0-indexed subsequent column (F_k)
275
+ const cp = k - 2; // 0-indexed preceding column (F_{k-1})
276
+ const sub = [];
277
+ const pre = [];
278
+ for (let i = 0; i < n; i++) {
279
+ const a = F[i][cs] ?? null;
280
+ const b = F[i][cp] ?? null;
281
+ if (isNum(a) && isNum(b)) {
282
+ sub.push(a);
283
+ pre.push(b);
284
+ }
285
+ }
286
+ const m = sub.length;
287
+ if (m < 2)
288
+ continue;
289
+ const hasTies = new Set(sub).size !== sub.length || new Set(pre).size !== pre.length;
290
+ if (hasTies) {
291
+ warnings.push(`Development year ${k}: tied factors; the Spearman statistic is approximate under ties`);
292
+ }
293
+ const r = averageRanks(sub);
294
+ const s = averageRanks(pre);
295
+ let d2 = 0;
296
+ for (let x = 0; x < m; x++)
297
+ d2 += (r[x] - s[x]) ** 2;
298
+ const Tk = 1 - (6 * d2) / (m * (m * m - 1));
299
+ columns.push({ k, pairs: m, statistic: Tk });
300
+ }
301
+ if (columns.length === 0)
302
+ return null;
303
+ const weightSum = columns.reduce((a, c) => a + (c.pairs - 1), 0);
304
+ if (weightSum <= 0)
305
+ return null;
306
+ const statistic = columns.reduce((a, c) => a + (c.pairs - 1) * c.statistic, 0) / weightSum;
307
+ const variance = 1 / weightSum;
308
+ const bound50 = 0.67 * Math.sqrt(variance);
309
+ return {
310
+ columns,
311
+ statistic,
312
+ variance,
313
+ bound50,
314
+ correlated: Math.abs(statistic) > bound50,
315
+ warnings,
316
+ };
317
+ }
318
+ /**
319
+ * Standardized Mack residuals r_ik = (F_ik - f_k) sqrt(C_ik) / sigma_k
320
+ * around the volume-weighted factors, with sigma^2_k estimated from the
321
+ * data (Mack 1994). Columns whose sigma^2 cannot be estimated from at least
322
+ * two factors — or where the factors show no dispersion at all — yield null
323
+ * residuals with a warning; extrapolated sigma^2 is deliberately NOT used
324
+ * here (a residual against an extrapolated scale is not a diagnostic).
325
+ *
326
+ * Structural property: within each column, sum_i residual_ik x sqrt(C_ik)
327
+ * = 0 exactly, because f_k is the volume-weighted average.
328
+ */
329
+ export function mackResiduals(tri) {
330
+ const warnings = [];
331
+ const { f, sigma2 } = mackEstimators(tri);
332
+ const cells = [];
333
+ const flaggedColumns = new Set();
334
+ for (let c = 0; c < tri.ages.length - 1; c++) {
335
+ const s2 = sigma2[c] ?? null;
336
+ const usable = isNum(s2) && s2 > 0;
337
+ if (!usable && !flaggedColumns.has(c)) {
338
+ flaggedColumns.add(c);
339
+ warnings.push(`Column ${tri.ages[c]}-${tri.ages[c + 1]}: sigma^2 is ${s2 === null ? "not estimable from the data" : "zero (no dispersion)"}; residuals are null there`);
340
+ }
341
+ for (let i = 0; i < tri.origins.length; i++) {
342
+ const c0 = tri.values[i]?.[c] ?? null;
343
+ const c1 = tri.values[i]?.[c + 1] ?? null;
344
+ if (!isNum(c0) || !isNum(c1) || c0 <= 0)
345
+ continue;
346
+ const factor = c1 / c0;
347
+ cells.push({
348
+ origin: tri.origins[i],
349
+ fromAge: tri.ages[c],
350
+ toAge: tri.ages[c + 1],
351
+ calendar: i + c + 1,
352
+ factor,
353
+ residual: usable ? ((factor - f[c]) * Math.sqrt(c0)) / Math.sqrt(s2) : null,
354
+ });
355
+ }
356
+ }
357
+ return { cells, warnings };
358
+ }
359
+ //# sourceMappingURL=diagnostics.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../src/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,166 @@
1
+ import type { ChainLadderResult } from "./types.js";
2
+ /**
3
+ * Discounting unpaid claim estimates per ASOP No. 20 ("Discounting of
4
+ * Property/Casualty Unpaid Claim Estimates", the revised standard effective
5
+ * June 1, 2026). The standard's discipline, encoded rather than footnoted:
6
+ *
7
+ * - Rate PROVENANCE is required input ({ source, asOfDate }): the standard
8
+ * requires the actuary to disclose the basis of the discount rates and the
9
+ * date as of which they were determined. This module refuses to discount
10
+ * without it.
11
+ * - Nominal and discounted amounts are reported SIDE BY SIDE, per origin and
12
+ * in total, with the effective discount factor between them. Neither
13
+ * replaces the other.
14
+ * - Risk margins are EXPLICIT ONLY: an optional `riskMargin` amount passes
15
+ * through the result untouched; no total in this module adds it in.
16
+ * Implicit margins (conservative rates, shaded patterns) are never
17
+ * manufactured here, and blending a margin into the discounted figure is
18
+ * left to the consumer to do visibly.
19
+ *
20
+ * Timing convention (payments assumed at period boundaries per the pattern):
21
+ * each expected payment occupies a development interval (fromMonths,
22
+ * toMonths] measured from the VALUATION DATE, i.e. the latest observed
23
+ * diagonal of the triangle behind the pattern. The `convention` option fixes
24
+ * the discount exponent for the interval's payment:
25
+ *
26
+ * - "end-period": t = toMonths / 12 years - the payment falls on the
27
+ * interval's closing boundary.
28
+ * - "mid-period": t = (fromMonths + toMonths) / 24 years - the standard
29
+ * uniform-payment approximation (cash spread evenly over the interval is
30
+ * discounted as if paid at its midpoint).
31
+ *
32
+ * Discount factors are (1 + rate)^(-t) with annual effective spot rates.
33
+ * Under a spot curve, a payment at time t uses the rate for the year
34
+ * containing t (t in (k-1, k] years uses spotByYear[k-1]); payments beyond
35
+ * the curve horizon use the LAST spot rate, with a warning.
36
+ */
37
+ export type DiscountConvention = "mid-period" | "end-period";
38
+ /** One expected payment, located relative to the valuation date. */
39
+ export interface PayoutCashflow {
40
+ /** Months after the valuation date at which the payment interval opens. */
41
+ fromMonths: number;
42
+ /** Months after the valuation date at which the interval closes. */
43
+ toMonths: number;
44
+ /** Expected payment within the interval (negative = expected recovery). */
45
+ amount: number;
46
+ }
47
+ export interface PayoutPatternRow {
48
+ origin: string;
49
+ /** Age (months) of the latest observed diagonal cell (the valuation age). */
50
+ latestAge: number;
51
+ /** The chain ladder unpaid this row's cashflows tie to (sum of amounts). */
52
+ unpaid: number;
53
+ cashflows: PayoutCashflow[];
54
+ }
55
+ export interface PayoutPattern {
56
+ rows: PayoutPatternRow[];
57
+ warnings: string[];
58
+ }
59
+ /**
60
+ * Expected future incremental payments per origin, derived from a chain
61
+ * ladder result: the increment for development interval (ages[j], ages[j+1]]
62
+ * is ultimate x (percentDeveloped[j+1] - percentDeveloped[j]), and the tail
63
+ * cash implied by a tail factor > 1 (ultimate x (1 - percentDeveloped at the
64
+ * last age)) is compressed into a single interval of the same width as the
65
+ * last observed development step, immediately after the last age (warned; a
66
+ * single-age triangle uses a 12-month tail interval). Each row's cashflow
67
+ * amounts sum to its unpaid by construction.
68
+ *
69
+ * `ages` must be the development ages the chain ladder ran on (the result
70
+ * does not carry them). Negative increments (percent developed not monotone,
71
+ * i.e. an LDF below 1) are kept and warned - never dropped or fabricated
72
+ * away.
73
+ */
74
+ export declare function payoutPatternFromChainLadder(cl: ChainLadderResult, ages: number[]): PayoutPattern;
75
+ /**
76
+ * Discount rates, annual effective:
77
+ * - "flat": one rate for every maturity.
78
+ * - "curve": spotByYear[k] is the spot rate for payments in year k+1 (times
79
+ * t in (k, k+1] years). Payments beyond the horizon use the last rate.
80
+ */
81
+ export type DiscountRates = {
82
+ kind: "flat";
83
+ annualRate: number;
84
+ } | {
85
+ kind: "curve";
86
+ spotByYear: number[];
87
+ };
88
+ /**
89
+ * Where the rates came from and when - REQUIRED, because ASOP 20 requires
90
+ * the actuary to disclose the basis of the discount rate(s) and the date as
91
+ * of which they were determined.
92
+ */
93
+ export interface RateProvenance {
94
+ /** e.g. "US Treasury CMT curve", "company investment yield per ASOP 20 5.3". */
95
+ source: string;
96
+ /** ISO date (yyyy-mm-dd) the rates were determined as of. */
97
+ asOfDate: string;
98
+ }
99
+ /** Explicit cashflows for one origin (the non-pattern input path). */
100
+ export interface OriginCashflows {
101
+ origin: string;
102
+ cashflows: PayoutCashflow[];
103
+ }
104
+ export interface DiscountUnpaidInput {
105
+ /** Payout pattern from payoutPatternFromChainLadder. Exactly one of pattern/cashflows. */
106
+ pattern?: PayoutPattern;
107
+ /** Explicit per-origin cashflows. Exactly one of pattern/cashflows. */
108
+ cashflows?: OriginCashflows[];
109
+ rates: DiscountRates;
110
+ provenance: RateProvenance;
111
+ convention: DiscountConvention;
112
+ /**
113
+ * Explicit risk margin (a dollar amount, not a rate). Carried through the
114
+ * result UNCHANGED and kept out of every total - never blended.
115
+ */
116
+ riskMargin?: number;
117
+ }
118
+ export interface DiscountedCashflowDetail {
119
+ /** Payment time in years from the valuation date under the chosen convention. */
120
+ timeYears: number;
121
+ amount: number;
122
+ /** Annual effective spot rate applied at timeYears. */
123
+ rate: number;
124
+ /** (1 + rate)^(-timeYears). */
125
+ discountFactor: number;
126
+ discounted: number;
127
+ }
128
+ export interface DiscountedUnpaidRow {
129
+ origin: string;
130
+ /** Undiscounted expected future payments (sum of cashflow amounts). */
131
+ nominal: number;
132
+ discounted: number;
133
+ /** nominal - discounted (the amount of discount taken). */
134
+ discount: number;
135
+ /** discounted / nominal; null when nominal is not positive. */
136
+ effectiveDiscountFactor: number | null;
137
+ cashflows: DiscountedCashflowDetail[];
138
+ }
139
+ export interface DiscountUnpaidResult {
140
+ method: "discountUnpaid";
141
+ convention: DiscountConvention;
142
+ rates: DiscountRates;
143
+ provenance: RateProvenance;
144
+ rows: DiscountedUnpaidRow[];
145
+ totals: {
146
+ nominal: number;
147
+ discounted: number;
148
+ discount: number;
149
+ effectiveDiscountFactor: number | null;
150
+ };
151
+ /**
152
+ * The caller's explicit risk margin, passed through untouched (null when
153
+ * none was supplied). NOT included in any total above: presenting it as a
154
+ * separate line is the consumer's job, per the explicit-only rule.
155
+ */
156
+ riskMargin: number | null;
157
+ warnings: string[];
158
+ }
159
+ /**
160
+ * Discounts expected future payments and reports nominal and discounted
161
+ * unpaid side by side, per origin and in total. See the module doc for the
162
+ * timing convention, curve lookup, and the ASOP 20 provenance/risk-margin
163
+ * rules this function enforces.
164
+ */
165
+ export declare function discountUnpaid(input: DiscountUnpaidInput): DiscountUnpaidResult;
166
+ //# sourceMappingURL=discounting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discounting.d.ts","sourceRoot":"","sources":["../src/discounting.ts"],"names":[],"mappings":"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"}