Tier 1 · Statutory Reserves

VM-20 Statutory Reserves (Life)

M-001 · lifecycle: monitoring · RAT-001-v7.0.2

Intended Use

VM-20 Statutory Reserves (Life) Compute principles-based statutory reserves for life insurance under NAIC Valuation Manual VM-20.

Deterministic and stochastic reserve computation under VM-20 PBR. Combines mortality, lapse, premium, and benefit assumptions with VM-20-specified scenario sets (CTE-based stochastic and deterministic reserve floors). Produces blue-book line items.


Components

Inputs, processing, outputs

data sources
DS-001 · DS-008 · DS-014
assumptions
A-001, A-002, A-003, A-004, A-010, A-011, A-070, A-071
engines
insmodel.L4.vm20_engine
insmodel.L4.mortality_engine
insmodel.L4.lapse_engine
insmodel.L4.premium_engine
insmodel.L4.benefit_engine
insmodel.L4.expense_engine
insmodel.L6.projection_orchestrator
contracts
statutory_reserves_v1
upstream
M-120
dimensions
D7

Methodology & Mechanics

Methodology

M-001 implements the NAIC Valuation Manual Section 20 (VM-20) principles-based reserve (PBR) architecture for individual life insurance. The model produces, for each policy or model-point cohort, four candidate reserves and selects the binding one per the VM-20 ordering rule:

  1. Net Premium Reserve (NPR) floor — VM-20 §3.C formulaic reserve using prescribed CRVM mechanics with the VM-20 mortality basis (the binding VM20ReserveEngine defaults to the SOA 2015 VBT nonsmoker table; 2017 CSO is the basis of the separate insmodel.L4.mortality_engine — see Key Assumptions A-001) and prescribed lapse/expense assumptions. Computed by insmodel.L4.vm20_engine consuming insmodel.L4.mortality_engine, lapse_engine, premium_engine, benefit_engine, and expense_engine outputs.
  2. Stochastic exclusion test (VM-20 §4.A) — applied via 7 prescribed interest-rate scenarios (level, ±100, ±200, ±300 bps) generated by VM20ReserveEngine._generate_prescribed_scenarios when a GOES scenario set is not supplied; GOES paths, when present, are mapped in via the internal VM20ScenarioAdapter (firmmodel/engines/vm20_scenario_adapter.py — an internal adapter, not a registered engine_registry entry). Pass/fail decision recorded as stochastic_exclusion_result.
  3. Deterministic exclusion test (VM-20 §4.B) — single prescribed deterministic scenario; pass/fail recorded as deterministic_exclusion_result.
  4. Deterministic reserve (VM-20 §5) — projected on the prescribed deterministic scenario set; principal reserve calculation when stochastic exclusion fails.
  5. Full stochastic reserve — CTE(70) of present-value losses across the GOES stochastic scenario set; computed when both exclusion tests fail and GOES is consumed (GOES scenarios → VM20ScenarioAdapterinsmodel.L4.vm20_engine via the goes_output_v1 contract).

The insmodel.L6.projection_orchestrator composes these engines per policy/cohort, runs all four candidates, and applies the §3.A reserve ordering: when both exclusion tests pass, the formulaic NPR is sufficient; when they fail, the binding reserve is max(NPR, deterministic, stochastic_CTE70). Output blue-book line items are emitted via the statutory_reserves_v1 contract.


Key Assumptions

Key Assumptions and Their Justification

The eight formally-bound A-NNN entries plus operational choices:

ID Name Value Derivation Justification for VM-20
A-001 Base mortality table VM-20 reserve path: 2015 VBT (engine default); mortality_engine basis: 2017 CSO published_source The binding VM20ReserveEngine defaults mortality_table='2015VBT' (legacy_metadata: "2015 VBT nonsmoker mortality basis"). The separate insmodel.L4.mortality_engine defaults base_table_year=2017 (CSO Loaded), the VM-20 §9.C.2 regulatory min/max basis. Do not read 2017 CSO as the reserve-engine basis without this distinction.
A-002 Mortality improvement scale MP-2021 (SOA) published_source Latest SOA-published improvement scale at the as-of date; applied to CSO base per VM-20 prescribed-assumption framework.
A-003 Select and ultimate factors SOA select tables by duration published_source Industry-standard duration grading; ASOP 48 §3.7 framework. Captures underwriting selection wear-off.
A-004 Annuity mortality adjustment IAM 2012 Basic + MP-2021 published_source Used only for annuity-blended life products; SOA IAM is the standard annuity mortality basis.
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; consistent with VM-20 §6 prescribed-assumption guardrails.
A-011 Base lapse curve (VA with guarantees) Duration-graded: 8% Y1 → 3% Y10+ data_calibrated Lower lapse than spread products reflects guarantee value; reduces dynamic lapse calibration error.
A-070 Per-policy maintenance expense $75–150/policy/year (product-dep) — not consumed by reserve engine data_calibrated Range based on industry FY2024 expense studies. Caveat: the deterministic reserve path in VM20ReserveEngine does not consume this figure — it applies a hardcoded 0.1%-of-face expense proxy (pv_expenses += face_amount * 0.001, commented "simplified prescribed assumption"). The $75–150 figure is a bound assumption awaiting wiring; see Limitations.
A-071 Expense inflation rate 2.5% p.a. published_source Long-run CPI proxy used in VM-20-compliant projections; documented in Decision 020 / DS-062 effective-date catalog.

Operational assumptions beyond the formal IDs: - Mortality improvement application: the VM-20 reserve path (vm20_reserve_engine._apply_mortality_improvement) applies an age-banded simplified improvement (5 bands: ≤30 → 0.5%/yr, ≤50 → 1.0%, ≤70 → 1.2%, ≤90 → 0.8%, 91+ → 0.3%). The separate mortality_engine instead applies a single scalar ((1 − improvement_rate)^years) — the scalar form is the mortality_engine behavior, not the reserve path. See Limitations. - COVID adjustment: applied as a flat multiplicative scalar on base qx; phase-out schedule per ASOP 25 credibility-weighted experience. - Underwriting class adjustments: multiplicative factors on base qx (ASOP 25 credibility framework); the 2017 CSO Loaded composite (A-001) does not include smoker/non-smoker splits — those are absorbed into UW class factors. - Scenario coverage: 7 prescribed interest-rate scenarios for stochastic exclusion (level, ±100, ±200, ±300 bps) when GOES is not consumed — _generate_prescribed_scenarios produces exactly 7 paths, matching legacy_metadata ("7 prescribed interest rate scenarios when GOES unavailable"); 1 prescribed deterministic scenario for deterministic exclusion + reserve. Full stochastic CTE(70) requires GOES consumption.


Output Snapshot

Output Snapshot

Deterministic single-policy run of VM20ReserveEngine v1.0.0 — reproducible, requires no live firm data (python scripts/model_snapshots.py M-001 in InsModel; asserted by tests/test_vm20_reserve.py). (vm20_reserve_engine.py declares version = "1.0.0"; the v2.0.0 label belongs to MortalityEngine. The snapshot script's header string previously read "v2.0.0" in error.)

Full stochastic reserve path ENABLED (INV-010, Decision 053). As of 2026-06-07 the snapshot runs the full stochastic reserve on the calibrated GOES scenario set (M-120, ratified RAT-120-v1.0.2: yield_amplifier=4.3). The held reserve is now max(NPR floor, deterministic, full-CTE(70)) computed from real GOES scenarios — no longer the deterministic floor alone. GOES provenance: python -m goes -n 50 -s 42 -y 30 (50 scenarios, 30-year horizon, seed 42, calibrated default), materialised via InsModel scripts/generate_goes_scenarios.py into firmmodel/data/goes_scenarios/ and consumed across the goes_output_v1 CSV contract.

Caveat — rate scenarios are calibrated/real; mortality is still fallback-qx. The snapshot's GOES interest-rate scenarios are now real and calibrated. The policy/mortality side still uses a MagicMock data_provider, so qx comes from the engine's simplified fallback formula (0.00005 * 1.09^(age−20)), not a loaded 2015 VBT / 2017 CSO table. The numbers demonstrate the enabled stochastic-reserve mechanics on calibrated scenarios + reproducibility, not table-grade mortality.

Input: issue age 65 · face $500,000 · TERM_LIFE · 20-pay · Male · valuation rate 3.5% · 50 calibrated GOES scenarios (seed 42, amp 4.3)

output value meaning
stochastic_exclusion_result FAIL SET not passed → stochastic path is in scope
deterministic_exclusion_result FAIL DET not passed → deterministic reserve required
formulaic_reserve (NPR) 5,913.51 net-premium reserve, CRVM-style
npr_floor 9,952.84 net-premium reserve floor
deterministic_reserve 34,776.39 deterministic PBR reserve (single prescribed path)
cte70_stochastic (SET diagnostic) 44,309.24 CTE(70) over the 7 SET scenarios — an exclusion-test diagnostic (unchanged; prescribed paths)
stochastic_reserve (full GOES CTE70) 39,682.22 CTE(70) over 50 calibrated GOES NAER paths — the held stochastic reserve
pbr_reserve (held) 39,682.22 max(NPR floor, deterministic, full-CTE(70)) — the held minimum reserve

Held-reserve diff (INV-010 enablement). Before enablement the held reserve was the deterministic floor: max(9,952.84, 34,776.39) = 34,776.39. With the full stochastic path enabled on calibrated GOES, the full-CTE(70) stochastic_reserve = 39,682.22 binds above the deterministic floor, so the held reserve is now 39,682.22 — an increase of +4,905.83 per policy (+14.1%). This is a genuine reserve change driven by the calibrated GOES rate-tail width (amp=4.3), not a hand-tuned number. Note the SET diagnostic cte70_stochastic (44,309.24, over the 7 prescribed exclusion paths) is a separate quantity from the held full-CTE(70) stochastic reserve (39,682.22, over the 50 calibrated GOES NAER paths) — the two use different scenario sets and are not directly comparable.

Captured 2026-06-07 · deterministic (seed 42), GOES rate scenarios calibrated/real, mortality fallback-qx.


Limitations

Limitations and Known Gaps

  1. Mortality improvement is simplified relative to published MP-2021. The VM-20 reserve path (vm20_reserve_engine._apply_mortality_improvement) applies improvement in 5 coarse age bands (≤30 0.5%, ≤50 1.0%, ≤70 1.2%, ≤90 0.8%, 91+ 0.3%) — age-banded, but coarser than the year-and-age-specific published Scale MP-2021. The scalar (single flat annual percentage) form applies only to the separate mortality_engine, not to the reserve path. For Tier-1 statutory reserves this banding introduces a small bias at age extremes; calibration tests bound the impact at <2% of total reserves.
  2. No smoker/non-smoker mortality distinction beyond UW class factors. The 2017 CSO Loaded composite (A-001) is used; smoker effects are absorbed into the multiplicative UW class factor rather than carried as a separate dimension. Acceptable for VM-20 because §9.C.2 permits Loaded composite as a regulatory minimum/maximum bound.
  3. No separate-account or variable product support. This card covers VM-20 (Life) only. Variable annuity reserves with guaranteed living/death benefits use VM-21 and live in M-002. Life products with separate-account components (e.g., variable universal life) are out of scope.
  4. Synthetic cohort distributions, not policy-level seriatim. The model runs on representative model-point cohorts (typically 50–500 per product class) with weighting for in-force composition. Policy-level seriatim runs are supported by the engines but not invoked in the standard reserve cycle for performance reasons.
  5. Cause-of-death decomposition absent. Pandemic shocks beyond the COVID scalar (e.g., specific-disease mortality spikes) cannot be modeled without additional dimensions. Acceptable for VM-20 because the standard does not require cause-specific reserves.
  6. GOES dependency for full stochastic CTE(70) — now satisfied by the calibrated set. The full stochastic reserve requires a GOES scenario set; INV-010 wires the calibrated GOES set (M-120, amp=4.3) into the canonical snapshot, so the held reserve now includes the full CTE(70). When the exclusion tests both pass, or if the GOES CSVs are absent (they are gitignored, regenerable via scripts/generate_goes_scenarios.py), the model falls back to deterministic-only output. Scenario depth in the canonical snapshot is 50 GOES scenarios — adequate to demonstrate the enabled path and bind CTE(70) above the floor, but a production reserve run would use the engine's larger n_goes_scenarios (default 1000) for a tighter tail estimate.

Tracked for ratification (output-changing, 2L re-ratification pending). INV-010 (Decision 053) enabled the full stochastic reserve path on the calibrated GOES set: the held reserve moved from the deterministic floor (34,776.39) to the full-CTE(70) (39,682.22, +14.1%). The gold snapshot was re-frozen and the diff documented above; the re-ratification of M-001 is a 2L effective-challenge act (per Decision 052/053) and is not performed here. Remaining items left for ratification, noted for transparency: bound assumptions A-070 (expense), A-003 (select/ultimate), A-004 (annuity mortality) are declared but not consumed by the reserve engine, which uses a 0.1%-of-face expense proxy and a fallback qx formula; full Tier-1 annual back-test per §10.2 incomplete (BT-013 cannot yet beat naive persistence); engine governance id collision INV-031 (mortality_engine carries governance id M-001; VM20ReserveEngine legacy_metadata is M-101V).


Validation Evidence

Validation Packet

Reserve ordering invariant (mechanically enforced in vm20_engine): pbr_reserve = max(NPR_floor, deterministic_reserve, formulaic_reserve) when at least one exclusion test fails; pbr_reserve = formulaic_reserve when both exclusion tests pass.

Mortality cross-check (in mortality_engine validation block): - Base qx values are cross-checked against published 2017 CSO tables by age × gender at engine init. - Final qx after experience + improvement + UW + select/ultimate adjustments is clipped to economic bounds [0.0001, 1.0] and compared against VM-20 §9.C regulatory min/max.

Track C cross-check (Gold Copy validation, per crosscheck_pricing_v1 contract): InsModel's insmodel.L4.mortality_engine outputs are compared against goldcopy.L7.cross_check for 6 representative scenarios (term life, whole life, ULSG, FIA-blend, MYGA, deferred annuity); tolerance band ±10bps on annual qx, ±2% on PV-reserved.

Gold test suite (Models/tests/test_engine_golden_cases.py): frozen reserve values for representative policy archetypes; CI-gated regression. Currency note (2026-06-06): the file is present in the live InsModel tree (42 assertions total) but the precise "35 frozen reserve values" count was not re-confirmed against the current file in this pass — treat the count as asserted-not-reverified pending a count audit.

Reserve-level sensitivity (Models/tests/test_vm20_sensitivity.py): 8 tests (re-confirmed 2026-06-06 — exactly 8 def test_ cases in the live file) covering shock direction + bounded magnitude on the top-3 assumptions (A-001 base mortality, A-002 MP-2021 improvement, A-010 base lapse curve). Added 2026-05-10 to close COND-001 sub-item 3.

Validation evidence pack (per MRM §10.5 item 5): canonical index at validation_evidence/M-001/v7.0.0/README.md maps each §10.5 sub-requirement to specific InsModel test files. Currency note (2026-06-06): the validation_evidence/M-001/v7.0.0/README.md pack is confirmed present in the live insightalm tree; the test-file mappings inside it were not line-by-line re-verified in this documentation pass.

Peer-review (AP-26 / VM-G): under Decision 028, peer-review attestation moved from 1L to 2L. The 2L technical ratification is vsdhaka/insightalm-mrm:ratifications/RAT-001-v7.0.0.yaml (decision: conditionally_approved, expires 2026-08-07). Conditions COND-001 (validation evidence) and COND-002 (change log) closed 2026-05-10; supersession to RAT-001-v7.0.1 with decision: approved is 2L's next step.

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

References

Regulatory: - NAIC Valuation Manual VM-20 §3.C — Net Premium Reserve calculation for life insurance. - NAIC Valuation Manual VM-20 §4.A — Stochastic exclusion test. - NAIC Valuation Manual VM-20 §4.B — Deterministic exclusion test. - NAIC Valuation Manual VM-20 §5 — Deterministic reserve methodology. - NAIC Valuation Manual VM-20 §9.C.2 — Mortality regulatory min/max bounds (2017 CSO Loaded basis). - NAIC Valuation Manual VM-G — Corporate-governance discipline for PBR (qualified actuary, peer review). - NAIC Valuation Manual VM-31 — Documentation taxonomy for the PBR Actuarial Report. - 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).

Actuarial standards: - ASOP No. 25 — Credibility Procedures (basis for experience factor application to base tables). - ASOP No. 48 — Life Settlements Mortality (select-and-ultimate period structure and grading framework). - ASOP No. 56 — Modeling (cross-cutting modeling standard: intended use, sensitivity, reliance, documentation).

Source data: - SOA 2015 Valuation Basic Table (VBT) — default reserve-engine mortality basis: VM20ReserveEngine defaults mortality_table='2015VBT' (legacy_metadata: "2015 VBT nonsmoker mortality basis"), and it is the table the binding VM-20 reserve path uses unless overridden. - 2017 Commissioners Standard Ordinary (CSO) Mortality Tables — NAIC, loaded composite per VM-20 §9.C; this is the basis of the separate insmodel.L4.mortality_engine (base_table_year default 2017), available as the VM-20 §9.C.2 regulatory min/max basis — not the reserve-engine default. - Society of Actuaries (SOA) Mortality Improvement Scale MP-2021.

Internal: - Decision 018 (MRM Framework) — Management/decisions/018-mrm-framework-multi-regime.md. - Decision 020 (Assumption Policy v1.2) — insightalm/modelling/charters/assumption_policy.md (effective-date catalog at DS-062). - Decision 023 (Policy-Based Binding Architecture) — Management/decisions/023-policy-based-binding-architecture.md. - Component legacy_metadata: ecosystem/InsModel/Models/firmmodel/governance/legacy_metadata/VM20ReserveEngine.yaml and MortalityEngine.yaml (extracted 2026-05-09 via Decision 023 Phase 3).


Change Log

Change Log

Canonical going-forward change log: M-001-changes.md (Keep-a-Changelog format, versioned with the InsModel release cycle). Code-side change history lives in git log of the component files; release notes in ecosystem/InsModel/CHANGELOG.md.

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).
  • 2026-05-09 — Tier-1 hand-authoring of Methodology, Key Assumptions and Their Justification, Limitations and Known Gaps, Validation Packet, and References sections from VM20ReserveEngine + MortalityEngine legacy_metadata + bound A-NNN entries. Stub marker advanced from to.
  • 2026-05-10 — closed RAT-001-v7.0.0 conditions COND-001 (validation evidence pack at validation_evidence/M-001/v7.0.0/README.md + 8 sensitivity tests in InsModel test_vm20_sensitivity.py) and COND-002 (this change log moved to canonical M-001-changes.md). 2L supersession ratification (RAT-001-v7.0.1, decision: approved) pending.
  • 2026-06-06 — code-grounded documentation-accuracy pass against vm20_reserve_engine.py / mortality_engine.py / vm20_scenario_adapter.py + legacy_metadata. Corrected prescribed scenario count 16→7 (Methodology, Output Snapshot, Limitation 6, Key Assumptions); corrected engine version label v2.0.0→v1.0.0; reconciled mortality basis (VM-20 reserve path = 2015 VBT default vs mortality_engine = 2017 CSO) across A-001 / Standards Coverage / Methodology / References; scoped the "scalar mortality improvement" claim to mortality_engine and documented the reserve path's 5-band age-banded improvement (Limitation 1, operational note); fixed scenario-adapter reference (VM20ScenarioAdapter, internal adapter — no insmodel.L5 entry); caveated A-070 ($75–150 not consumed; engine uses 0.1%-of-face proxy); added Output-Snapshot honesty caveat (MagicMock provider → fallback qx formula, not table-grade); added Validation-Packet currency notes; noted gated/ratification items under Limitations. No model outputs, back-test numbers, or validation results were fabricated or changed.
  • 2026-06-07INV-010 (Decision 053): full stochastic reserve path ENABLED on the calibrated GOES set. Wired the canonical M-001 snapshot to a real GOESDataProvider over a calibrated GOES scenario set (M-120 / RAT-120-v1.0.2, yield_amplifier=4.3; python -m goes -n 50 -s 42 -y 30), replacing the prior deterministic-floor-only / mock-GOES state. Output-changing: held pbr_reserve moved 34,776.39 → 39,682.22 (+14.1%) because the full-CTE(70) stochastic_reserve (39,682.22 over 50 calibrated GOES NAER paths) binds above the deterministic floor. Re-froze the M-001 gold snapshot (scripts/model_snapshots.py M-001) and documented the diff (Output Snapshot, Limitation 6, ratification note). Also fixed a latent contract-boundary bug: GOESDataProvider.get_scenario_data now translates raw GOES tenor labels ('3M'/'10.0Y') to canonical InsModel keys ('treasury_3m'/'treasury_10y') via TENOR_MAP — without this the adapter/orchestrator NAER computation silently matched zero tenors. Numbers are computed from real calibrated scenarios; none hand-tuned. 2L re-ratification pending.

2L Inventory Review

Open findings (2)

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.

HIGH INV-010 · P6 · capability-gap

Full stochastic reserves are gated on GOES (M-120) — today they run on the deterministic floor

VM-20 (M-001), VM-21 (M-002), and C-3 (M-050) all require the full prescribed ESG scenario set for their held *stochastic* reserve. Because GOES (M-120) is under_development, these engines compute the SET/SSA *exclusion diagnostics* but hold max(deterministic, NPR) — the stochastic CTE shown is a diagnostic, not the held amount. The platform's "stochastic" reserves are effectively deterministic today.

Recommendation: Sequence M-120 calibration (INV-009) as the unlock for the Tier-1 stochastic chain, then enable the full-scenario path in VM20ReserveEngine / C3Phase2Engine and re-validate the held reserves. Until then, state plainly (done in cards) that the held reserve is the deterministic floor.

HIGH INV-029 · P5 · validation-gap

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.


Validation Coverage

Per-tier expectations

Per MRM Framework §10.2 + §10.3, this model's regulatory_frameworks tag list activates the following overlays:

asop_56 vm_g_vm20 sr_26_2 pcaob_as_2501 internal
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

Ratification

Ratified — RAT-001-v7.0.2

Latest ratification on file: RAT-001-v7.0.2. Authored by 2L (mrm-peer-reviewer) per Decision 028 charter §5 Pattern A.