LDTI / ASC 944 GAAP Reserves Compute GAAP reserves and DAC under ASC 944 / LDTI (Long-Duration Targeted Improvements).
LDTI net premium reserve, market-risk-benefit (MRB) fair value, deferred profit liability, and DAC amortization. Quarterly remeasurement with NPR / true-up cycle. MRB fair-value change is split per ASC 820 between OCI (the instrument-specific own-credit / NPR component) and net income (all other changes); the OCI leg is a documented deferred refinement pending FinView credit-spread integration (card limitation #2). AF-01 audit gap acknowledged: experience data plumbing for remeasurement is unbuilt; consumer documentation states this limitation per Decision 020 §AP-19.
Inputs, processing, outputs
- data sources
- DS-001 · DS-008
- assumptions
- A-001, A-002, A-010, A-070, A-071, A-072
- engines
-
insmodel.L4.reserve_engine_ldti
insmodel.L4.reserve_engine_gaap
insmodel.L4.dac_engine
insmodel.L4.dac_projection
insmodel.L4.mortality_engine
insmodel.L4.lapse_engine
insmodel.L4.expense_engine
insmodel.L6.multi_basis_orchestrator
- contracts
- gaap_reserves_v1
- upstream
- M-120
- dimensions
- D6
Methodology
M-020 implements ASC 944 with the FASB Long-Duration Targeted Improvements (LDTI) update for non-participating traditional and limited-pay long-duration insurance contracts. The model produces, per cohort (issue-year × product-type grouping), four GAAP financial-statement quantities:
- Net Premium Reserve (NPR) — present value of future benefits less present value of future net premiums, discounted at the upper-medium grade fixed-income rate (single-A spot curve) locked at the cohort's issue date for OCI tracking. Computed by
insmodel.L4.reserve_engine_ldticonsumingmortality_engine,lapse_engine, andexpense_engineoutputs. - Reserve remeasurement — at each reporting period, cash flow assumptions are updated based on actual-to-expected experience (the "true-up cycle"). The remeasurement catch-up adjustment flows to net income for cash flow assumption updates and to OCI for discount rate updates; this split is one of the central mechanics LDTI introduced.
- Contractual Service Margin (CSM) roll-forward — implemented by the GAAP engine (
insmodel.L4.reserve_engine_gaap,GAAPReserveEngine). At inception,initial_csm = max(0, EPV(premiums) − EPV(benefits))(the unearned profit margin); if benefits exceed premiums, aloss_componentis recognized immediately. Each period the CSM accretes interest at the locked-in rate, absorbs adverse experience variances, and amortizes via the coverage-units method (csm_amortization = csm_balance / remaining_coverage_units— straight-line over remaining coverage). When adverse movements would drive the CSM negative, it is floored at 0 and the excess flows to the loss component (_roll_forward_csm/_amortize_csm). This CSM mechanic is the core of the GAAP engine path the Output Snapshot exercises. - Deferred Acquisition Costs (DAC) amortization — amortized on a constant-level basis over the cohort's expected coverage period (insurance in-force, simplified to straight-line for many product classes), computed by
dac_engine/dac_projection. LDTI removed the pre-LDTI gross-profits-based amortization model. - Deferred Profit Liability (DPL) — for limited-pay contracts, the unearned profit at issue is held as a liability and recognized over the coverage period. DPL is computed by the LDTI reserve engine (
insmodel.L4.reserve_engine_ldti,reserve_engine.py) via_calculate_dpl(a simplified margin:benefit_reserve_current × (1 − net_premium_ratio) × dpl_margin_factor, floored at 0), distinct from the CSM roll-forward in the GAAP engine.
The insmodel.L6.multi_basis_orchestrator composes these engines per cohort, runs both the GAAP-NPR and (separately) the statutory VM-20 reserves (M-001), and emits cohort-level results via the gaap_reserves_v1 contract. Cohort grouping is capped: NPR cohorts cannot offset losses on one issue year against gains on another, so the cohort definition (issue-year × product-class) is structurally narrower than statutory VM-20's life-only segmentation.
Market Risk Benefits (MRBs) are NOT YET MODELLED in M-020. Under LDTI, separate-account guarantees (e.g., GMxB on variable annuities) receive fair-value measurement, with the fair-value change split between OCI (instrument-specific credit risk) and net income (all other changes). No MRB / fair-value / own-credit code exists in any M-020 engine (gaap_reserve_engine.py, reserve_engine.py, multi_basis_orchestrator.py) — the only GMxB-related code in the InsModel stack lives in c3_phase2_engine.py, which backs C-3 / VM-21 capital (M-002), not M-020. MRB fair-value measurement and the ASC 820 own-credit OCI split are therefore out of scope / pending for M-020 (see Limitations); they are not implemented and produce no M-020 outputs today.
Key Assumptions and Their Justification
The six formally-bound A-NNN entries plus operational choices:
| ID | Name | Value | Derivation | Justification for LDTI |
|---|---|---|---|---|
| A-001 | Base mortality table | 2017 CSO Loaded (Male/Female) | published_source | Same base table as VM-20; LDTI is silent on mortality basis selection, so the regulatory-minimum 2017 CSO is used as the conservative baseline. Cohort experience overlays this base at remeasurement. |
| A-002 | Mortality improvement scale | MP-2021 (SOA) | published_source | Latest SOA-published improvement scale at the as-of date; applied to CSO base. Updated annually with each SOA scale release. |
| A-010 | Base lapse curve (spread liability) | Duration-graded: 15% Y1 → 5% Y10+ | data_calibrated | Calibrated against 5-year industry experience for MYGA/SPDA-type products. LDTI lapse-rate updates flow through the remeasurement true-up cycle. |
| A-070 | Per-policy maintenance expense | $75–150/policy/year (product-dep) | data_calibrated | Range based on industry FY2024 expense studies; product-specific values selected at cohort issue. Expense assumption updates trigger reserve remeasurement. |
| A-071 | Expense inflation rate | 2.5% p.a. | published_source | Long-run CPI proxy; aligns with Decision 020 / DS-062 effective-date catalog. |
| A-072 | Commission first-year rate | 5–8% of premium (product-dependent) | published_source | Used in DAC capitalization. First-year commission is a DAC-eligible cost; level commission (years 2+) is expensed. |
Operational assumptions beyond the formal IDs:
- Discount rate: upper-medium grade (single-A) spot curve from DS-001 (canonical market data). Locked at issue for OCI tracking; current at each remeasurement for the net-income split.
- Cohort grouping: issue-year × product-class. Cohort definitions cannot be re-cut mid-life. Conservative grouping (narrower cohorts) is acceptable under LDTI; broader grouping that produces offsetting effects is not.
- DAC amortization basis: constant-level over expected coverage period. Coverage period proxied by 80% in-force duration for most product classes; explicit projection for limited-pay.
- MRB measurement: not modelled in M-020 — there is no MRB fair-value code in any M-020 engine. Separate-account guarantee capital is handled by M-002 (VM-21, c3_phase2_engine.py); the GAAP MRB fair-value measurement and the ASC 820 own-credit OCI split are out of scope / pending here (see Limitations).
- Experience-adjustment inputs: actual_deaths, actual_lapses, actual_expenses (optional cohort inputs), expense_ae_factor, and the SOA A/E integration (soa_ae_experience_enabled → _apply_soa_ae_adjustment / soa_experience_config). These mechanics exist in GAAPReserveEngine but default to expected experience when no actual feeds are supplied (see Limitations #1).
Output Snapshot
Deterministic single-cohort run of GAAPReserveEngine — reproducible, no live firm data (python scripts/model_snapshots.py M-020 in InsModel; asserted by tests/mrm/test_gold_tier0.py::TestGoldGAAPReserve).
Version-label convention: the snapshot header reads "GAAPReserveEngine v2.0.0", which is the InsModel release tag used uniformly across snapshot headers (the same convention M-001 uses for VM20ReserveEngine v2.0.0). It is not the class version attribute, which is version = "1.0.0" on GAAPReserveEngine in gaap_reserve_engine.py. The change-log / limitations below refer to the v1.0.0 class baseline. No output numbers change with this clarification; the snapshot script is deterministic with fixed params and reproduces the card values.
Input: 1,000-policy cohort · face $100K · locked-in rate 4.0% · current rate 4.5% · mortality 0.5%/yr · lapse 3.0%/yr · premium $2,000/policy · 20-yr coverage · period 1
| output | value | meaning |
|---|---|---|
| beginning_reserve | 0.00 | new cohort — no opening liability |
| npr (net premium ratio) | 0.2500 | EPV benefits ÷ EPV premiums, locked at issue (ASC 944-40) |
| ending_reserve | 1,019,274.48 | period-1 liability for future policy benefits (LFPB) |
| aoci_impact | −45,399.71 | current-rate (4.5%) remeasurement vs locked (4.0%) → lower reserve → negative OCI |
The net premium ratio (0.25) is the LDTI engine's core mechanic: benefits are funded as a constant fraction of premiums, locked at issue and unlocked only on assumption review. The AOCI impact isolates LDTI's distinctive current-rate remeasurement — discounting the same benefit cash flows at the higher current rate (4.5% vs the locked 4.0%) lowers the liability, booking a negative adjustment to other comprehensive income while net income continues to run on the locked rate. That split is the LDTI signal this engine is built to reproduce.
Captured 2026-06-04 · deterministic, no live data.
Limitations and Known Gaps
- Experience-adjustment mechanics exist but are inert without actual feeds (AF-01). The remeasurement true-up requires actual-vs-expected mortality, lapse, and expense data. The plumbing is built in
GAAPReserveEngine: optional cohort inputsactual_deaths/actual_lapses/actual_expenses, theexpense_ae_factorparameter, and SOA A/E integration (soa_ae_experience_enabled→_apply_soa_ae_adjustment/soa_experience_config); the engine'schange_historyrecords v1.2.0 "Added expense_ae_factor for non-zero expense experience adjustments (AF-01 closure)". The remaining gap: when no actual experience feed is supplied, the engine falls back to expected experience (placeholders/defaults), so the experience-adjustment term is zero/neutral and remeasurement does not reflect realized mortality/lapse/expense. Wiring live actual-vs-expected feeds into the true-up is the substantive AF-01 closure and remains tracked — seeinsightalm/governance/audit_log.yaml::AF-01. Ratification does NOT close AF-01. - MRB (Market Risk Benefit) fair-value measurement is NOT YET MODELLED — out of scope / pending. Under ASC 944-40 / ASC 820, a Market Risk Benefit (separate-account guarantee such as GMxB) is measured at fair value, and its period change is split between OCI (the instrument-specific own-credit component) and net income (all other changes). No MRB code exists in any M-020 engine —
grepformrb/market_risk_benefit/fair_valueacrossgaap_reserve_engine.py,reserve_engine.py, andmulti_basis_orchestrator.pyreturns nothing. The only GMxB code in the InsModel stack is inc3_phase2_engine.py, which backs C-3 / VM-21 capital (M-002), not the M-020 GAAP reserve path. Both the MRB fair-value base mechanic and the own-credit OCI-vs-net-income split are therefore unbuilt; M-020 produces no MRB output today. Implementing MRB fair value and the ASC 820 own-credit OCI split is an output-changing enhancement tracked for ratification (see note at end of this section), not a built-but-deferred-leg. - Cohort grouping is product-class-coarse. Issue-year × product-class cohorts may be narrower than competitor groupings (some insurers group by issue-year + product-class + rider). Narrower cohorts produce more conservative reserves (less offsetting); ASC 944 permits this. Documented for transparency.
- No reinsurance ceded mechanics yet. Cohort-level reserves are gross of reinsurance. Ceded reserves require a separate cohort and offsetting cash flows; deferred until reinsurance modeling is brought into the InsModel stack.
- DAC for limited-pay contracts uses straight-line proxy. ASC 944 permits a more granular schedule based on the in-force pattern; the straight-line approximation is a v1.0.0 simplification. Refinement is tracked as a Phase 2 enhancement.
- DPL release schedule shares the NPR mortality assumption. A separate DPL-specific mortality assumption (e.g., for products with substantively different mortality experience) would require an A-NNN extension. None currently warranted.
- CSM amortization uses straight-line coverage units. The GAAP engine amortizes the CSM via
csm_balance / remaining_coverage_units(per the enginechange_historynote "CSM amortization uses straight-line coverage units, not a more complex pattern"). A more granular coverage-unit pattern (e.g., discounted or in-force-weighted units) is a possible future refinement. - Remeasurement uses a mid-point-duration discount approximation. The current-rate remeasurement (
_remeasure_at_current_rate) approximates with((1+locked)/(1+current))^(remaining/2)rather than a full cash-flow re-projection at the current curve — tracked for ratification (see note below).
Output-changing enhancements tracked for ratification (not applied in this documentation pass): - Implement MRB fair-value measurement and the ASC 820 own-credit OCI-vs-net-income split in the M-020 engine path (today no MRB code exists; building it produces new model outputs). - Wire live actual-vs-expected experience feeds into the remeasurement true-up (the substantive AF-01 closure) so remeasurement reflects realized mortality/lapse/expense rather than expected fallbacks. - Produce real Tier-1 validation evidence required by INV-029 (back-test vs disclosed/filed results, sensitivity suite, challenger comparison) for M-020. - Improve the remeasurement method beyond the mid-point-duration approximation to a full cash-flow re-projection at the current curve.
Validation Packet
Reserve identity (mechanically enforced in reserve_engine_ldti): for each cohort, NPR(t) = PV(future benefits at t) − PV(future net premiums at t) where the discount curve is the cohort's locked single-A spot curve.
Cohort-level loss-recognition test: an additional check ensures reserves never go negative at the cohort level (a hallmark of LDTI rigor). Tested in test_ldti_cohort_invariants.py::TestNonNegativeReserve.
Track C cross-check (Gold Copy validation, per crosscheck_pricing_v1 contract): InsModel's LDTI cohort reserves are compared against goldcopy.L7.cross_check for 4 representative LDTI archetypes (term-15y, term-20y, whole-life, limited-pay-20yr); tolerance ±2% on PV-reserved, ±$1k/policy on NPR.
Gold test suite (Models/tests/test_engine_golden_cases.py): frozen reserve values for representative LDTI policy archetypes; CI-gated regression — any reserve drift outside tolerance breaks the build.
Sensitivity packet (intended to close COND-001 in RAT-020-v1.0.0): canonical index at validation_evidence/M-020/v1.0.0/README.md (present in the evidence tree) is intended to map each §10.5 item-5 sub-requirement to specific InsModel test files. Link-integrity / completeness of the referenced sensitivity, Track C cross-check, and gold suites should be confirmed against the actual evidence tree and InsModel test files before relying on them for sign-off — this card's documentation pass verified the README is present but did not re-run or re-confirm each referenced artifact.
INV-029 Tier-1 evidence (pending). Full Tier-1 validation under INV-029 — back-test vs disclosed/filed results, a complete sensitivity suite, and a challenger comparison — is pending. Producing this evidence is an output/work item tracked for ratification (see Limitations note); it is not represented as complete here.
Peer-review (AP-26 / ASC 944): under Decision 028, peer-review attestation moved from 1L to 2L. The 2L technical ratification is vsdhaka/insightalm-mrm:ratifications/RAT-020-v1.0.0.yaml (decision: approved, expires 2027-05-25).
Model-vs-reality back-testing (SR 26-2 ongoing; canonical record backtest_results.yaml): under Decision 048 (A4-30) the aggregate reserve is reconciled to filed reserves through the firm-calibrated book — each firm's canonical segment-reserve decomposition (DS-073), not a notional cohort. Record BT-012 (status: covered): the reserve aggregate reconciles to reported policy reserves by reportable segment for the segment-grade cohort, superseding the earlier Mock-notional 0% (BT-011). Predictive record BT-013: at quarterly cadence a reserve roll-forward is benchmarked against naive persistence — persistence is a tight ~3.4% q/q baseline that a structural roll-forward beats only where segment flows are cleanest; the data needed to beat it robustly (segment-level flows / disclosed θ sensitivities) is named, not assumed.
References
Accounting / regulatory: - FASB ASC 944 — Financial Services—Insurance. - FASB ASU 2018-12 — Targeted Improvements to the Accounting for Long-Duration Contracts (LDTI). - FASB ASC 820 — Fair Value Measurement (for MRB and instrument-specific-credit-risk split). - Federal Reserve SR 26-2 (April 2026, supersedes SR 11-7) — Model risk management for banks (applicable to InsightALM platform consumers via Decision 018 MRM Framework). - PCAOB AS 2501 — Auditing Accounting Estimates.
Actuarial standards: - ASOP No. 25 — Credibility Procedures (basis for experience factor application). - ASOP No. 56 — Modeling (cross-cutting modeling standard).
Source data: - 2017 Commissioners Standard Ordinary (CSO) Mortality Tables — NAIC, loaded composite. - Society of Actuaries (SOA) Mortality Improvement Scale MP-2021.
Internal:
- Decision 018 (MRM Framework) — Management/decisions/018-mrm-framework-multi-regime.md.
- Decision 019 (Data Policy) — for the AF-01 experience-data plumbing gap.
- Decision 020 (Assumption Policy v1.2) — insightalm/modelling/charters/assumption_policy.md.
- Decision 023 (Policy-Based Binding Architecture) — Management/decisions/023-policy-based-binding-architecture.md.
- Decision 028 (Independent 2L MRM Workspace) — moves peer-review from 1L to 2L.
Change Log
Canonical going-forward change log: M-020-changes.md (present in model_cards/; Keep-a-Changelog format, versioned with the InsModel release cycle). Code-side change history lives in git log of the component files and in the engine legacy_metadata change_history (GAAPReserveEngine.yaml); release notes in ecosystem/InsModel/CHANGELOG.md.
Registry reconciliation: the model registry lifecycle.documentation_pack currently flags change_log: missing for M-020 even though the canonical M-020-changes.md is present. The §10.5 item-7 change-log requirement is satisfied by the present file; the registry flag should be updated to reflect it (registry-data fix, tracked separately — not a card change).
Pre-baseline authorship history of this card:
- 2026-05-08 — stub created from registry data per Decision 023 Phase 5 / B-07.
- 2026-05-09 — added DS-018 + DS-062 references and Standards Coverage section per Stage B (DG-005 closure); Methodology / Key Assumptions / Validation sections remain stubbed for actuarial author follow-on.
- 2026-05-25 — Tier-1 hand-authoring of Methodology, Key Assumptions and Their Justification, Limitations and Known Gaps, Validation Packet, and References sections. Removed DS-018 + DS-062 lines (registry has DS-001 + DS-008 only; the 2026-05-09 additions were template residue from M-001 stub-copy). Stub marker advanced from
to. Phase 1 "documentation-first" path: this hand-authoring is part of the v1.0.0 baseline, issued BEFORE 2L ratification. - 2026-06-04 — Validation Packet: added the model-vs-reality back-test reference (BT-012 firm-calibrated reserve reconciliation; BT-013 predictive vs persistence) per A4-30 / Decision 048. SR 26-2 ongoing back-testing now documented on the card.
- 2026-06-05 — Sharpened known-limitation #2 (MRB instrument-specific-credit-risk OCI-vs-net-income split): made the ASC 820 / ASC 944-40 own-credit (NPR) mechanics explicit, grounded in PRU/MET own-credit MRB adjustments, and stated the OCI leg is tracked-not-closed pending FinView credit-spread integration. Registry M-020 description updated to mirror. No code or output change — the OCI split remains a deferred refinement (the FinView credit-spread feed is the blocker). Draft pending 2L re-attestation.
- 2026-06-06 — Documentation accuracy pass grounded in actual engine code (
gaap_reserve_engine.py,reserve_engine.py,multi_basis_orchestrator.py) andGAAPReserveEngine.yamllegacy_metadata. (1) Corrected the MRB over-claim: MRB / market-risk-benefit / fair-value machinery does NOT exist in any M-020 engine (grep formrb/market_risk_benefit/fair_valuereturns nothing; the only GMxB code is inc3_phase2_engine.pybacking M-002 C-3/VM-21). Description, Methodology, and Limitation #2 rewritten so MRB fair value and the ASC 820 own-credit OCI split are honestly described as NOT YET MODELLED / out of scope-pending, not a built-mechanic-with-a-deferred-OCI-leg. (2) Reframed AF-01 (Limitation #1): experience-adjustment plumbing (actual_deaths/actual_lapses/actual_expenses,expense_ae_factor, SOA A/E via_apply_soa_ae_adjustment/soa_experience_config; legacy_metadata v1.2.0 "AF-01 closure") exists in code but is inert without actual experience feeds (defaults fall back to expected). (3) Added the CSM mechanic the GAAP engine centers on (initial_csm, coverage-units straight-linecsm_amortization,loss_componenton CSM exhaustion) to Methodology, and clarified that DPL lives in the LDTI reserve engine (reserve_engine.py) while CSM lives ingaap_reserve_engine.py. (4) Documented the engine version-label convention: "v2.0.0" is the InsModel release tag (same as M-001's snapshot header); theGAAPReserveEngineclass attribute isversion = "1.0.0". No output numbers changed. (5) Flagged INV-029 Tier-1 evidence as pending, noted validation-link integrity unverified, and reconciled theM-020-changes.mdpointer against the registrychange_log: missingflag. Output-changing enhancements (MRB build, live A/E feeds, INV-029 evidence, full-curve remeasurement) listed under Limitations as tracked-for-ratification, not applied. Draft pending 2L re-attestation.
Open findings (1)
Independent 2nd-line review (INV-2026-06) — implemented capability vs registered scope. Each carries a recommended fix and is tracked in insightalm-mrm until closed.
Validation evidence + change logs missing across most of the inventory
Only M-001/M-020/M-050 carried full documentation packs before this pass. Most models record validation_evidence: missing and change_log: missing with peer_review: pending. Gold tests freeze behaviour but many assert only structural invariants (e.g. reserve>0), not correctness against external truth. The flagship T0-vs-10-K match is circular (BV-032).
Recommendation: For each Tier-1 model: produce a validation-evidence pack (back-test vs disclosed results once BV-032 re-calibration lands, sensitivity suite, challenger comparison), a change log, and a 2L ratification. Sequence behind BV-032 (firm-data) for anything needing 10-K reconciliation.
Per-tier expectations
Per MRM Framework §10.2 + §10.3, this model's regulatory_frameworks tag list activates the following overlays:
| component | tier-1 expectation | status |
|---|---|---|
| Registry entry | required | present |
| Model card (§10.5 doc pack) | required | present |
| Validation evidence | required | present |
| Change log | required | present |
| Independent effective challenge (2L) | required | attested |
Ratified — RAT-020-v1.0.0
Latest ratification on file: RAT-020-v1.0.0. Authored by 2L (mrm-peer-reviewer) per Decision 028 charter §5 Pattern A.