@actuarial-ts/data 0.3.0 → 0.5.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.
- package/README.md +86 -4
- package/dist/annualDevelopment.d.ts +94 -0
- package/dist/annualDevelopment.d.ts.map +1 -0
- package/dist/annualDevelopment.js +188 -0
- package/dist/annualDevelopment.js.map +1 -0
- package/dist/csv.d.ts +11 -1
- package/dist/csv.d.ts.map +1 -1
- package/dist/csv.js +9 -2
- package/dist/csv.js.map +1 -1
- package/dist/diagnosticInput.d.ts +14 -0
- package/dist/diagnosticInput.d.ts.map +1 -0
- package/dist/diagnosticInput.js +51 -0
- package/dist/diagnosticInput.js.map +1 -0
- package/dist/diagnosticReview.d.ts +68 -0
- package/dist/diagnosticReview.d.ts.map +1 -0
- package/dist/diagnosticReview.js +370 -0
- package/dist/diagnosticReview.js.map +1 -0
- package/dist/exposure.d.ts +44 -0
- package/dist/exposure.d.ts.map +1 -0
- package/dist/exposure.js +113 -0
- package/dist/exposure.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/lossRun.d.ts +1 -1
- package/dist/lossRun.d.ts.map +1 -1
- package/dist/lossRun.js +9 -4
- package/dist/lossRun.js.map +1 -1
- package/dist/review.d.ts +22 -0
- package/dist/review.d.ts.map +1 -1
- package/dist/review.js +25 -3
- package/dist/review.js.map +1 -1
- package/package.json +4 -3
- package/src/annualDevelopment.ts +278 -0
- package/src/csv.ts +17 -2
- package/src/diagnosticInput.ts +79 -0
- package/src/diagnosticReview.ts +547 -0
- package/src/exposure.ts +136 -0
- package/src/index.ts +4 -0
- package/src/lossRun.ts +10 -5
- package/src/review.ts +54 -3
package/README.md
CHANGED
|
@@ -1,19 +1,33 @@
|
|
|
1
1
|
# @actuarial-ts/data
|
|
2
2
|
|
|
3
3
|
Data ingestion and ASOP No. 23 data-quality review for the
|
|
4
|
-
[actuarial-ts](../core) SDK. Pure functions,
|
|
5
|
-
|
|
4
|
+
[actuarial-ts](../core) SDK. Pure functions, fully typed, with runtime schemas
|
|
5
|
+
at object boundaries.
|
|
6
6
|
|
|
7
7
|
- `parseCsv(text)` — minimal RFC 4180-subset CSV parser (quoted fields,
|
|
8
|
-
escaped quotes, embedded commas/newlines, BOM, CRLF/LF)
|
|
8
|
+
escaped quotes, embedded commas/newlines, BOM, CRLF/LF); the result's
|
|
9
|
+
`rowLines` gives each row's 1-based physical start line in the file.
|
|
9
10
|
- `parseLossRunCsv(text)` — loss-run import to `ClaimSnapshot[]` with
|
|
10
|
-
per-row validation errors (1-based
|
|
11
|
+
per-row validation errors (errors cite 1-based physical file lines,
|
|
12
|
+
header = line 1).
|
|
13
|
+
- `annualDevelopmentToClaimSnapshots(rows, options)` — converts claim
|
|
14
|
+
valuations known only to annual precision without hiding the derived-date,
|
|
15
|
+
report-year, or duplicate-row conventions.
|
|
16
|
+
- `parseExposureCsv(text)` — imports earned premium and/or exposure units by
|
|
17
|
+
origin; extra source measures remain extra rather than being relabeled.
|
|
11
18
|
- `triangleFromLongFormat(rows, { kind })` — pivots long-format
|
|
12
19
|
`(origin, age, value)` rows into a `Triangle`.
|
|
13
20
|
- `reviewClaimData(claims, { asOfDate? })` / `reviewTriangles(paid, incurred)`
|
|
14
21
|
— the ASOP No. 23-oriented review; every check performed is listed in the
|
|
15
22
|
report, pass or fail, so the actuary's disclosure can state what WAS
|
|
16
23
|
reviewed, not just what was found.
|
|
24
|
+
- `validateDiagnosticDataset(value)` /
|
|
25
|
+
`validateAndReconcileDiagnosticExposures(value)` /
|
|
26
|
+
`runValidatedMetricDiagnostics(...)` — Zod-validated boundaries plus
|
|
27
|
+
stable-key exposure reconciliation for generic diagnostic rows.
|
|
28
|
+
- `reviewDiagnosticData(snapshots, exposures, options)` — quarterly aggregate,
|
|
29
|
+
exposure, layer, grouping, and cached-formula checks using the same
|
|
30
|
+
`DataReviewReport` status contract, with optional structured finding context.
|
|
17
31
|
|
|
18
32
|
## Checks
|
|
19
33
|
|
|
@@ -57,6 +71,74 @@ const { paid, incurred } = buildTriangles(claims, {
|
|
|
57
71
|
const triangleReview = reviewTriangles(paid, incurred);
|
|
58
72
|
```
|
|
59
73
|
|
|
74
|
+
### Annual-precision claim data
|
|
75
|
+
|
|
76
|
+
Do not label a source year as an exact accident or evaluation date. Normalize
|
|
77
|
+
the source fields to annual rows, then let the adapter derive calendar-year-end
|
|
78
|
+
compatibility dates and return the convention as a disclosure finding:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import { annualDevelopmentToClaimSnapshots } from "@actuarial-ts/data";
|
|
82
|
+
|
|
83
|
+
const conversion = annualDevelopmentToClaimSnapshots(
|
|
84
|
+
[{
|
|
85
|
+
claimId: "1995-000001",
|
|
86
|
+
originYear: 1995,
|
|
87
|
+
evaluationYear: 1996,
|
|
88
|
+
paidToDate: 100,
|
|
89
|
+
incurredToDate: 140,
|
|
90
|
+
status: "open",
|
|
91
|
+
}],
|
|
92
|
+
{ duplicatePolicy: "reject" },
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
console.log(conversion.conventions.derivedDateConvention); // calendar-year-end
|
|
96
|
+
console.log(conversion.findings); // date and inferred-report-year disclosures
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The default duplicate policy is `reject`. Selecting `combine` is an explicit
|
|
100
|
+
judgment: same-claim/same-year paid and incurred values are summed, and the
|
|
101
|
+
combined record is open if any component is open. The adapter reports how many
|
|
102
|
+
groups and rows were combined.
|
|
103
|
+
|
|
104
|
+
### Exposure data
|
|
105
|
+
|
|
106
|
+
`parseExposureCsv` requires `origin` plus `earned_premium`, `exposure_units`,
|
|
107
|
+
or both. Each numeric measure may be blank independently. For example, a
|
|
108
|
+
source containing insurance-years and gross written premium should expose only
|
|
109
|
+
the insurance-years to the SDK unless an earned-premium transformation has
|
|
110
|
+
actually been performed:
|
|
111
|
+
|
|
112
|
+
```csv
|
|
113
|
+
origin,exposure_units,gross_written_premium
|
|
114
|
+
2024,125000,42000000
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The extra GWP column is retained in the source file but ignored by the parser;
|
|
118
|
+
it is never silently loaded as `earnedPremium`.
|
|
119
|
+
|
|
120
|
+
### Quarterly diagnostic review
|
|
121
|
+
|
|
122
|
+
`reviewDiagnosticData` lists all 19 stable codes in
|
|
123
|
+
`DIAGNOSTIC_REVIEW_CHECK_CODES`. The suite covers duplicate aggregate and
|
|
124
|
+
exposure keys; invalid or mismatched development ages; valuation-before-origin;
|
|
125
|
+
count identities; paid/incurred and cumulative movement checks; reopen
|
|
126
|
+
signals; layer ordering and control totals; both sides of the loss/exposure
|
|
127
|
+
join; zero/incomplete exposure; grouping consistency; and lightweight cached
|
|
128
|
+
formula provenance.
|
|
129
|
+
|
|
130
|
+
The existing `DataCheck.details: string[]` field is unchanged. New checks also
|
|
131
|
+
populate `DataCheck.findings` with optional `origin`, `valuation`, `ageMonths`,
|
|
132
|
+
`group`, `sourceFile`, and `sourceRow` context so consumers need not parse
|
|
133
|
+
prose. Severities and numeric tolerances are caller-configurable. Optional
|
|
134
|
+
checks without configuration report `not-evaluated`, never a misleading pass.
|
|
135
|
+
|
|
136
|
+
For unknown objects, call `validateDiagnosticDataset` (or the convenience
|
|
137
|
+
`runValidatedMetricDiagnostics`) before the core analysis. For workbook input,
|
|
138
|
+
extract rows with the host's spreadsheet tooling and pass cached formula
|
|
139
|
+
metadata to the review; this package deliberately does not add a heavyweight
|
|
140
|
+
XLSX dependency.
|
|
141
|
+
|
|
60
142
|
These utilities are designed to support the actuary's compliance with
|
|
61
143
|
ASOP No. 23; responsibility for compliance remains with the credentialed
|
|
62
144
|
actuary.
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import type { ClaimSnapshot } from "@actuarial-ts/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* A claim valuation known only to annual precision.
|
|
5
|
+
*
|
|
6
|
+
* `incurredToDate` is deliberately named for the interpretation already made
|
|
7
|
+
* by the caller. Source-specific adapters must document how their fields were
|
|
8
|
+
* mapped to paid and incurred before calling this function.
|
|
9
|
+
*/
|
|
10
|
+
export declare const annualClaimDevelopmentRowSchema: z.ZodEffects<z.ZodObject<{
|
|
11
|
+
claimId: z.ZodString;
|
|
12
|
+
originYear: z.ZodNumber;
|
|
13
|
+
reportYear: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
evaluationYear: z.ZodNumber;
|
|
15
|
+
paidToDate: z.ZodNumber;
|
|
16
|
+
incurredToDate: z.ZodNumber;
|
|
17
|
+
status: z.ZodEnum<["open", "closed"]>;
|
|
18
|
+
}, "strict", z.ZodTypeAny, {
|
|
19
|
+
claimId: string;
|
|
20
|
+
originYear: number;
|
|
21
|
+
evaluationYear: number;
|
|
22
|
+
paidToDate: number;
|
|
23
|
+
incurredToDate: number;
|
|
24
|
+
status: "open" | "closed";
|
|
25
|
+
reportYear?: number | undefined;
|
|
26
|
+
}, {
|
|
27
|
+
claimId: string;
|
|
28
|
+
originYear: number;
|
|
29
|
+
evaluationYear: number;
|
|
30
|
+
paidToDate: number;
|
|
31
|
+
incurredToDate: number;
|
|
32
|
+
status: "open" | "closed";
|
|
33
|
+
reportYear?: number | undefined;
|
|
34
|
+
}>, {
|
|
35
|
+
claimId: string;
|
|
36
|
+
originYear: number;
|
|
37
|
+
evaluationYear: number;
|
|
38
|
+
paidToDate: number;
|
|
39
|
+
incurredToDate: number;
|
|
40
|
+
status: "open" | "closed";
|
|
41
|
+
reportYear?: number | undefined;
|
|
42
|
+
}, {
|
|
43
|
+
claimId: string;
|
|
44
|
+
originYear: number;
|
|
45
|
+
evaluationYear: number;
|
|
46
|
+
paidToDate: number;
|
|
47
|
+
incurredToDate: number;
|
|
48
|
+
status: "open" | "closed";
|
|
49
|
+
reportYear?: number | undefined;
|
|
50
|
+
}>;
|
|
51
|
+
export type AnnualClaimDevelopmentRow = z.infer<typeof annualClaimDevelopmentRowSchema>;
|
|
52
|
+
export type AnnualDuplicatePolicy = "reject" | "combine";
|
|
53
|
+
export interface AnnualDevelopmentOptions {
|
|
54
|
+
/**
|
|
55
|
+
* Same-claim, same-year records are ambiguous. The safe default rejects
|
|
56
|
+
* them. `combine` treats them as components of one dollar stream: paid and
|
|
57
|
+
* incurred are summed and the combined record is open when any component is
|
|
58
|
+
* open. The returned finding makes that judgment visible.
|
|
59
|
+
*/
|
|
60
|
+
duplicatePolicy?: AnnualDuplicatePolicy;
|
|
61
|
+
}
|
|
62
|
+
export type AnnualDevelopmentFindingCode = "annual-dates-derived" | "report-year-inferred" | "duplicate-snapshots-combined";
|
|
63
|
+
export interface AnnualDevelopmentFinding {
|
|
64
|
+
code: AnnualDevelopmentFindingCode;
|
|
65
|
+
severity: "disclosure" | "warning";
|
|
66
|
+
message: string;
|
|
67
|
+
affectedClaims?: number;
|
|
68
|
+
affectedGroups?: number;
|
|
69
|
+
affectedRows?: number;
|
|
70
|
+
}
|
|
71
|
+
export interface AnnualDevelopmentConventions {
|
|
72
|
+
sourceDatePrecision: "year";
|
|
73
|
+
derivedDateConvention: "calendar-year-end";
|
|
74
|
+
missingReportYearConvention: "earliest-evaluation-year";
|
|
75
|
+
duplicatePolicy: AnnualDuplicatePolicy;
|
|
76
|
+
}
|
|
77
|
+
export interface AnnualDevelopmentConversion {
|
|
78
|
+
claims: ClaimSnapshot[];
|
|
79
|
+
findings: AnnualDevelopmentFinding[];
|
|
80
|
+
conventions: AnnualDevelopmentConventions;
|
|
81
|
+
sourceRowCount: number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Converts year-precision claim valuations into the exact-date
|
|
85
|
+
* `ClaimSnapshot` contract without pretending the source supplied exact
|
|
86
|
+
* dates. Every derived date uses calendar year-end, and the convention is
|
|
87
|
+
* returned alongside the claims for disclosure.
|
|
88
|
+
*
|
|
89
|
+
* Missing report years are inferred as the earliest evaluation year for the
|
|
90
|
+
* claim. Duplicate claim/evaluation-year rows are rejected unless the caller
|
|
91
|
+
* explicitly selects the auditable `combine` policy.
|
|
92
|
+
*/
|
|
93
|
+
export declare function annualDevelopmentToClaimSnapshots(input: readonly AnnualClaimDevelopmentRow[], options?: AnnualDevelopmentOptions): AnnualDevelopmentConversion;
|
|
94
|
+
//# sourceMappingURL=annualDevelopment.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annualDevelopment.d.ts","sourceRoot":"","sources":["../src/annualDevelopment.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiCxC,CAAC;AAEL,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,+BAA+B,CAAC,CAAC;AAExF,MAAM,MAAM,qBAAqB,GAAG,QAAQ,GAAG,SAAS,CAAC;AAEzD,MAAM,WAAW,wBAAwB;IACvC;;;;;OAKG;IACH,eAAe,CAAC,EAAE,qBAAqB,CAAC;CACzC;AAMD,MAAM,MAAM,4BAA4B,GACpC,sBAAsB,GACtB,sBAAsB,GACtB,8BAA8B,CAAC;AAEnC,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,4BAA4B,CAAC;IACnC,QAAQ,EAAE,YAAY,GAAG,SAAS,CAAC;IACnC,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,4BAA4B;IAC3C,mBAAmB,EAAE,MAAM,CAAC;IAC5B,qBAAqB,EAAE,mBAAmB,CAAC;IAC3C,2BAA2B,EAAE,0BAA0B,CAAC;IACxD,eAAe,EAAE,qBAAqB,CAAC;CACxC;AAED,MAAM,WAAW,2BAA2B;IAC1C,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,QAAQ,EAAE,wBAAwB,EAAE,CAAC;IACrC,WAAW,EAAE,4BAA4B,CAAC;IAC1C,cAAc,EAAE,MAAM,CAAC;CACxB;AAqBD;;;;;;;;;GASG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,SAAS,yBAAyB,EAAE,EAC3C,OAAO,GAAE,wBAA6B,GACrC,2BAA2B,CAyJ7B"}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { ReservingError } from "@actuarial-ts/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
/**
|
|
4
|
+
* A claim valuation known only to annual precision.
|
|
5
|
+
*
|
|
6
|
+
* `incurredToDate` is deliberately named for the interpretation already made
|
|
7
|
+
* by the caller. Source-specific adapters must document how their fields were
|
|
8
|
+
* mapped to paid and incurred before calling this function.
|
|
9
|
+
*/
|
|
10
|
+
export const annualClaimDevelopmentRowSchema = z
|
|
11
|
+
.object({
|
|
12
|
+
claimId: z.string().trim().min(1),
|
|
13
|
+
originYear: z.number().int().min(1).max(9999),
|
|
14
|
+
reportYear: z.number().int().min(1).max(9999).optional(),
|
|
15
|
+
evaluationYear: z.number().int().min(1).max(9999),
|
|
16
|
+
paidToDate: z.number().finite(),
|
|
17
|
+
incurredToDate: z.number().finite(),
|
|
18
|
+
status: z.enum(["open", "closed"]),
|
|
19
|
+
})
|
|
20
|
+
.strict()
|
|
21
|
+
.superRefine((row, ctx) => {
|
|
22
|
+
if (row.evaluationYear < row.originYear) {
|
|
23
|
+
ctx.addIssue({
|
|
24
|
+
code: z.ZodIssueCode.custom,
|
|
25
|
+
path: ["evaluationYear"],
|
|
26
|
+
message: "evaluationYear must not precede originYear",
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
if (row.reportYear !== undefined && row.reportYear < row.originYear) {
|
|
30
|
+
ctx.addIssue({
|
|
31
|
+
code: z.ZodIssueCode.custom,
|
|
32
|
+
path: ["reportYear"],
|
|
33
|
+
message: "reportYear must not precede originYear",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
if (row.reportYear !== undefined && row.evaluationYear < row.reportYear) {
|
|
37
|
+
ctx.addIssue({
|
|
38
|
+
code: z.ZodIssueCode.custom,
|
|
39
|
+
path: ["evaluationYear"],
|
|
40
|
+
message: "evaluationYear must not precede reportYear",
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
const annualDevelopmentOptionsSchema = z
|
|
45
|
+
.object({ duplicatePolicy: z.enum(["reject", "combine"]).default("reject") })
|
|
46
|
+
.strict();
|
|
47
|
+
function yearEnd(year) {
|
|
48
|
+
return `${String(year).padStart(4, "0")}-12-31`;
|
|
49
|
+
}
|
|
50
|
+
function issueMessage(error) {
|
|
51
|
+
return error.issues
|
|
52
|
+
.map((issue) => {
|
|
53
|
+
const path = issue.path.length > 0 ? `${issue.path.join(".")}: ` : "";
|
|
54
|
+
return `${path}${issue.message}`;
|
|
55
|
+
})
|
|
56
|
+
.join("; ");
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Converts year-precision claim valuations into the exact-date
|
|
60
|
+
* `ClaimSnapshot` contract without pretending the source supplied exact
|
|
61
|
+
* dates. Every derived date uses calendar year-end, and the convention is
|
|
62
|
+
* returned alongside the claims for disclosure.
|
|
63
|
+
*
|
|
64
|
+
* Missing report years are inferred as the earliest evaluation year for the
|
|
65
|
+
* claim. Duplicate claim/evaluation-year rows are rejected unless the caller
|
|
66
|
+
* explicitly selects the auditable `combine` policy.
|
|
67
|
+
*/
|
|
68
|
+
export function annualDevelopmentToClaimSnapshots(input, options = {}) {
|
|
69
|
+
const parsedOptions = annualDevelopmentOptionsSchema.safeParse(options);
|
|
70
|
+
if (!parsedOptions.success) {
|
|
71
|
+
throw new ReservingError("UNSUPPORTED_VALUE", `Invalid annual development options: ${issueMessage(parsedOptions.error)}`);
|
|
72
|
+
}
|
|
73
|
+
const { duplicatePolicy } = parsedOptions.data;
|
|
74
|
+
const rows = input.map((candidate, inputIndex) => {
|
|
75
|
+
const parsed = annualClaimDevelopmentRowSchema.safeParse(candidate);
|
|
76
|
+
if (!parsed.success) {
|
|
77
|
+
throw new ReservingError("SHAPE", `Annual claim development row ${inputIndex + 1}: ${issueMessage(parsed.error)}`);
|
|
78
|
+
}
|
|
79
|
+
return parsed.data;
|
|
80
|
+
});
|
|
81
|
+
const byClaim = new Map();
|
|
82
|
+
for (const row of rows) {
|
|
83
|
+
const existing = byClaim.get(row.claimId);
|
|
84
|
+
if (existing === undefined) {
|
|
85
|
+
byClaim.set(row.claimId, {
|
|
86
|
+
originYear: row.originYear,
|
|
87
|
+
reportYears: new Set(row.reportYear === undefined ? [] : [row.reportYear]),
|
|
88
|
+
rows: [row],
|
|
89
|
+
});
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
if (existing.originYear !== row.originYear) {
|
|
93
|
+
throw new ReservingError("SHAPE", `Claim ${row.claimId} has conflicting origin years ${existing.originYear} and ${row.originYear}`);
|
|
94
|
+
}
|
|
95
|
+
if (row.reportYear !== undefined)
|
|
96
|
+
existing.reportYears.add(row.reportYear);
|
|
97
|
+
existing.rows.push(row);
|
|
98
|
+
}
|
|
99
|
+
const findings = [
|
|
100
|
+
{
|
|
101
|
+
code: "annual-dates-derived",
|
|
102
|
+
severity: "disclosure",
|
|
103
|
+
affectedRows: rows.length,
|
|
104
|
+
message: "The source supplies calendar years, not exact dates; accident, report and evaluation dates were derived at calendar year-end.",
|
|
105
|
+
},
|
|
106
|
+
];
|
|
107
|
+
const claims = [];
|
|
108
|
+
let inferredReportYears = 0;
|
|
109
|
+
let duplicateGroups = 0;
|
|
110
|
+
let duplicateRows = 0;
|
|
111
|
+
for (const [claimId, group] of byClaim) {
|
|
112
|
+
if (group.reportYears.size > 1) {
|
|
113
|
+
throw new ReservingError("SHAPE", `Claim ${claimId} has conflicting report years ${[...group.reportYears].sort((a, b) => a - b).join(", ")}`);
|
|
114
|
+
}
|
|
115
|
+
const firstEvaluationYear = group.rows.reduce((earliest, row) => Math.min(earliest, row.evaluationYear), group.rows[0].evaluationYear);
|
|
116
|
+
const suppliedReportYear = group.reportYears.values().next().value;
|
|
117
|
+
const reportYear = suppliedReportYear ?? firstEvaluationYear;
|
|
118
|
+
if (suppliedReportYear === undefined)
|
|
119
|
+
inferredReportYears += 1;
|
|
120
|
+
if (reportYear > firstEvaluationYear) {
|
|
121
|
+
throw new ReservingError("SHAPE", `Claim ${claimId} has report year ${reportYear} after its first evaluation year ${firstEvaluationYear}`);
|
|
122
|
+
}
|
|
123
|
+
const byEvaluationYear = new Map();
|
|
124
|
+
for (const row of group.rows) {
|
|
125
|
+
const sameYear = byEvaluationYear.get(row.evaluationYear);
|
|
126
|
+
if (sameYear === undefined)
|
|
127
|
+
byEvaluationYear.set(row.evaluationYear, [row]);
|
|
128
|
+
else
|
|
129
|
+
sameYear.push(row);
|
|
130
|
+
}
|
|
131
|
+
for (const evaluationYear of [...byEvaluationYear.keys()].sort((a, b) => a - b)) {
|
|
132
|
+
const sameYear = byEvaluationYear.get(evaluationYear);
|
|
133
|
+
if (sameYear.length > 1) {
|
|
134
|
+
duplicateGroups += 1;
|
|
135
|
+
duplicateRows += sameYear.length;
|
|
136
|
+
if (duplicatePolicy === "reject") {
|
|
137
|
+
throw new ReservingError("SHAPE", `Claim ${claimId} has ${sameYear.length} snapshots in evaluation year ${evaluationYear}; choose duplicatePolicy "combine" only after reviewing the source ambiguity`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const paidToDate = sameYear.reduce((sum, row) => sum + row.paidToDate, 0);
|
|
141
|
+
const incurredToDate = sameYear.reduce((sum, row) => sum + row.incurredToDate, 0);
|
|
142
|
+
const caseReserve = incurredToDate - paidToDate;
|
|
143
|
+
if (!Number.isFinite(paidToDate) ||
|
|
144
|
+
!Number.isFinite(incurredToDate) ||
|
|
145
|
+
!Number.isFinite(caseReserve)) {
|
|
146
|
+
throw new ReservingError("SHAPE", `Claim ${claimId} evaluation year ${evaluationYear}: combined paid, incurred, and case reserve must remain finite`);
|
|
147
|
+
}
|
|
148
|
+
claims.push({
|
|
149
|
+
claimId,
|
|
150
|
+
accidentDate: yearEnd(group.originYear),
|
|
151
|
+
reportDate: yearEnd(reportYear),
|
|
152
|
+
evaluationDate: yearEnd(evaluationYear),
|
|
153
|
+
paidToDate,
|
|
154
|
+
caseReserve,
|
|
155
|
+
status: sameYear.some((row) => row.status === "open") ? "open" : "closed",
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (inferredReportYears > 0) {
|
|
160
|
+
findings.push({
|
|
161
|
+
code: "report-year-inferred",
|
|
162
|
+
severity: "disclosure",
|
|
163
|
+
affectedClaims: inferredReportYears,
|
|
164
|
+
message: "Report year was absent and was inferred as each claim's earliest evaluation year.",
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
if (duplicateGroups > 0) {
|
|
168
|
+
findings.push({
|
|
169
|
+
code: "duplicate-snapshots-combined",
|
|
170
|
+
severity: "warning",
|
|
171
|
+
affectedGroups: duplicateGroups,
|
|
172
|
+
affectedRows: duplicateRows,
|
|
173
|
+
message: "Same-claim, same-year rows were combined by summing paid and incurred; the combined status is open when any component is open.",
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
return {
|
|
177
|
+
claims,
|
|
178
|
+
findings,
|
|
179
|
+
conventions: {
|
|
180
|
+
sourceDatePrecision: "year",
|
|
181
|
+
derivedDateConvention: "calendar-year-end",
|
|
182
|
+
missingReportYearConvention: "earliest-evaluation-year",
|
|
183
|
+
duplicatePolicy,
|
|
184
|
+
},
|
|
185
|
+
sourceRowCount: rows.length,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//# sourceMappingURL=annualDevelopment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"annualDevelopment.js","sourceRoot":"","sources":["../src/annualDevelopment.ts"],"names":[],"mappings":"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"}
|
package/dist/csv.d.ts
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Supported: comma delimiter, quoted fields, doubled quotes ("") as escaped
|
|
5
5
|
* quotes inside quoted fields, commas and newlines inside quoted fields,
|
|
6
6
|
* CRLF and LF row endings, a leading UTF-8 BOM, and a tolerated trailing
|
|
7
|
-
* newline. Lines that are completely empty outside quotes are skipped
|
|
7
|
+
* newline. Lines that are completely empty outside quotes are skipped —
|
|
8
|
+
* skipped lines still advance the physical line count reported in
|
|
9
|
+
* `rowLines`, so later rows' reported start lines stay correct.
|
|
8
10
|
*
|
|
9
11
|
* Deliberately NOT here: header handling, type coercion, and shape
|
|
10
12
|
* validation. Ragged rows are preserved as-is — validation is the caller's
|
|
@@ -13,6 +15,14 @@
|
|
|
13
15
|
export interface CsvParseResult {
|
|
14
16
|
/** The parsed grid (rows x columns). Ragged rows preserved as-is. */
|
|
15
17
|
rows: string[][];
|
|
18
|
+
/**
|
|
19
|
+
* Physical 1-based line number in the input where each row of `rows`
|
|
20
|
+
* STARTS. Parallel to `rows`. Differs from index+1 whenever blank lines
|
|
21
|
+
* were skipped or a quoted field contains newlines — callers reporting
|
|
22
|
+
* row-level problems must use this, not the grid index, or their
|
|
23
|
+
* diagnostics point at the wrong line of the file.
|
|
24
|
+
*/
|
|
25
|
+
rowLines: number[];
|
|
16
26
|
/**
|
|
17
27
|
* Structural problems the parser recovered from. Content is still parsed
|
|
18
28
|
* leniently — a warning here means the OUTPUT may not mean what the file's
|
package/dist/csv.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"csv.d.ts","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,MAAM,WAAW,cAAc;IAC7B,qEAAqE;IACrE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAC;IACjB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;OAMG;IACH,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,6EAA6E;AAC7E,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,CA6FrD"}
|
package/dist/csv.js
CHANGED
|
@@ -4,7 +4,9 @@
|
|
|
4
4
|
* Supported: comma delimiter, quoted fields, doubled quotes ("") as escaped
|
|
5
5
|
* quotes inside quoted fields, commas and newlines inside quoted fields,
|
|
6
6
|
* CRLF and LF row endings, a leading UTF-8 BOM, and a tolerated trailing
|
|
7
|
-
* newline. Lines that are completely empty outside quotes are skipped
|
|
7
|
+
* newline. Lines that are completely empty outside quotes are skipped —
|
|
8
|
+
* skipped lines still advance the physical line count reported in
|
|
9
|
+
* `rowLines`, so later rows' reported start lines stay correct.
|
|
8
10
|
*
|
|
9
11
|
* Deliberately NOT here: header handling, type coercion, and shape
|
|
10
12
|
* validation. Ragged rows are preserved as-is — validation is the caller's
|
|
@@ -16,12 +18,14 @@ export function parseCsv(text) {
|
|
|
16
18
|
if (s.length > 0 && s.charCodeAt(0) === 0xfeff)
|
|
17
19
|
s = s.slice(1);
|
|
18
20
|
const rows = [];
|
|
21
|
+
const rowLines = [];
|
|
19
22
|
const warnings = [];
|
|
20
23
|
let row = [];
|
|
21
24
|
let field = "";
|
|
22
25
|
let inQuotes = false;
|
|
23
26
|
let fieldWasQuoted = false;
|
|
24
27
|
let line = 1;
|
|
28
|
+
let rowStartLine = 1;
|
|
25
29
|
let quoteOpenedAtLine = 0;
|
|
26
30
|
const endField = () => {
|
|
27
31
|
row.push(field);
|
|
@@ -35,6 +39,7 @@ export function parseCsv(text) {
|
|
|
35
39
|
return;
|
|
36
40
|
endField();
|
|
37
41
|
rows.push(row);
|
|
42
|
+
rowLines.push(rowStartLine);
|
|
38
43
|
row = [];
|
|
39
44
|
};
|
|
40
45
|
let i = 0;
|
|
@@ -74,12 +79,14 @@ export function parseCsv(text) {
|
|
|
74
79
|
i++;
|
|
75
80
|
endRow();
|
|
76
81
|
line++;
|
|
82
|
+
rowStartLine = line;
|
|
77
83
|
i++;
|
|
78
84
|
continue;
|
|
79
85
|
}
|
|
80
86
|
if (ch === "\n") {
|
|
81
87
|
endRow();
|
|
82
88
|
line++;
|
|
89
|
+
rowStartLine = line;
|
|
83
90
|
i++;
|
|
84
91
|
continue;
|
|
85
92
|
}
|
|
@@ -96,6 +103,6 @@ export function parseCsv(text) {
|
|
|
96
103
|
"input was consumed into a single field; check the file for a stray or unescaped quote");
|
|
97
104
|
}
|
|
98
105
|
endRow();
|
|
99
|
-
return { rows, warnings };
|
|
106
|
+
return { rows, rowLines, warnings };
|
|
100
107
|
}
|
|
101
108
|
//# sourceMappingURL=csv.js.map
|
package/dist/csv.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"csv.js","sourceRoot":"","sources":["../src/csv.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type DiagnosticExposureRow, type DiagnosticLossRow, type MetricDiagnosticsResult, type ReconciledDiagnosticExposures, type RunMetricDiagnosticsInput } from "@actuarial-ts/core";
|
|
2
|
+
export interface ValidatedDiagnosticDataset {
|
|
3
|
+
losses: DiagnosticLossRow[];
|
|
4
|
+
/** Omitted when the caller did not supply exposure data. */
|
|
5
|
+
exposures?: DiagnosticExposureRow[];
|
|
6
|
+
}
|
|
7
|
+
/** Zod-validates unknown diagnostic rows at the data package boundary. */
|
|
8
|
+
export declare function validateDiagnosticDataset(value: unknown): ValidatedDiagnosticDataset;
|
|
9
|
+
/** Validates unknown exposure rows, then applies core's stable-key reconciliation. */
|
|
10
|
+
export declare function validateAndReconcileDiagnosticExposures(value: unknown): ReconciledDiagnosticExposures;
|
|
11
|
+
export type ValidatedMetricDiagnosticsOptions = Omit<RunMetricDiagnosticsInput, "losses" | "exposures">;
|
|
12
|
+
/** Convenience boundary: validate unknown rows, then run the dependency-free core engine. */
|
|
13
|
+
export declare function runValidatedMetricDiagnostics(dataset: unknown, options: ValidatedMetricDiagnosticsOptions): MetricDiagnosticsResult;
|
|
14
|
+
//# sourceMappingURL=diagnosticInput.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticInput.d.ts","sourceRoot":"","sources":["../src/diagnosticInput.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAC5B,KAAK,6BAA6B,EAClC,KAAK,yBAAyB,EAC/B,MAAM,oBAAoB,CAAC;AA+B5B,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,4DAA4D;IAC5D,SAAS,CAAC,EAAE,qBAAqB,EAAE,CAAC;CACrC;AAED,0EAA0E;AAC1E,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,0BAA0B,CAYpF;AAED,sFAAsF;AACtF,wBAAgB,uCAAuC,CACrD,KAAK,EAAE,OAAO,GACb,6BAA6B,CAG/B;AAED,MAAM,MAAM,iCAAiC,GAAG,IAAI,CAAC,yBAAyB,EAAE,QAAQ,GAAG,WAAW,CAAC,CAAC;AAExG,6FAA6F;AAC7F,wBAAgB,6BAA6B,CAC3C,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,iCAAiC,GACzC,uBAAuB,CAGzB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ReservingError, reconcileDiagnosticExposureKeys, runMetricDiagnostics, } from "@actuarial-ts/core";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const measuresSchema = z.record(z.number().nullable());
|
|
4
|
+
const diagnosticLossRowSchema = z.object({
|
|
5
|
+
id: z.string().min(1),
|
|
6
|
+
group: z.string().min(1),
|
|
7
|
+
origin: z.string().min(1),
|
|
8
|
+
valuation: z.string().min(1),
|
|
9
|
+
ageMonths: z.number(),
|
|
10
|
+
policyPeriod: z.string().min(1).optional(),
|
|
11
|
+
dimensions: z.unknown().optional(),
|
|
12
|
+
measures: measuresSchema,
|
|
13
|
+
}).strict();
|
|
14
|
+
const diagnosticExposureRowSchema = z.object({
|
|
15
|
+
key: z.string().min(1),
|
|
16
|
+
group: z.string().min(1),
|
|
17
|
+
origin: z.string().min(1),
|
|
18
|
+
valuation: z.string().min(1).optional(),
|
|
19
|
+
measures: measuresSchema,
|
|
20
|
+
complete: z.boolean().optional(),
|
|
21
|
+
dimensions: z.unknown().optional(),
|
|
22
|
+
}).strict();
|
|
23
|
+
const diagnosticDatasetSchema = z.object({
|
|
24
|
+
losses: z.array(diagnosticLossRowSchema),
|
|
25
|
+
exposures: z.array(diagnosticExposureRowSchema).optional(),
|
|
26
|
+
}).strict();
|
|
27
|
+
/** Zod-validates unknown diagnostic rows at the data package boundary. */
|
|
28
|
+
export function validateDiagnosticDataset(value) {
|
|
29
|
+
const parsed = diagnosticDatasetSchema.safeParse(value);
|
|
30
|
+
if (!parsed.success) {
|
|
31
|
+
const details = parsed.error.issues
|
|
32
|
+
.map((issue) => `${issue.path.length > 0 ? issue.path.join(".") : "$"}: ${issue.message}`)
|
|
33
|
+
.join("; ");
|
|
34
|
+
throw new ReservingError("SHAPE", `Invalid diagnostic dataset: ${details}`);
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
losses: parsed.data.losses,
|
|
38
|
+
...(parsed.data.exposures !== undefined ? { exposures: parsed.data.exposures } : {}),
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/** Validates unknown exposure rows, then applies core's stable-key reconciliation. */
|
|
42
|
+
export function validateAndReconcileDiagnosticExposures(value) {
|
|
43
|
+
const validated = validateDiagnosticDataset({ losses: [], exposures: value });
|
|
44
|
+
return reconcileDiagnosticExposureKeys(validated.exposures ?? []);
|
|
45
|
+
}
|
|
46
|
+
/** Convenience boundary: validate unknown rows, then run the dependency-free core engine. */
|
|
47
|
+
export function runValidatedMetricDiagnostics(dataset, options) {
|
|
48
|
+
const validated = validateDiagnosticDataset(dataset);
|
|
49
|
+
return runMetricDiagnostics({ ...options, ...validated });
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=diagnosticInput.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnosticInput.js","sourceRoot":"","sources":["../src/diagnosticInput.ts"],"names":[],"mappings":"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"}
|