Assumption Validation & Rule Engine Design
Assumption validation is the deterministic control plane that governs how every mortality, lapse, and interest input is vetted, fingerprinted, and substantiated before it reaches a reserving or capital model. As regulators intensify scrutiny of model governance, the actuarial profession has shifted from static, spreadsheet-driven review toward code-first rule engines that satisfy actuarial precision, compliance traceability, and engineering reproducibility at once. This page is the reference architecture for that engine: how inputs are parsed, how business rules route exceptions, how thresholds adapt to drift, and how validated assumptions become examiner-ready filing artifacts. It anchors a family of deep-dive guides on rate validation, behavioral modeling, economic alignment, and threshold tuning.
Regulatory and Operational Context
The assumption set is the single largest source of model risk in a life insurer’s balance sheet. A mispriced mortality improvement scale or an unvalidated lapse curve does not surface as a syntax error; it surfaces years later as reserve inadequacy, a failed capital stress test, or a restated filing. Every major solvency framework now treats assumption governance as a first-class control rather than an actuarial footnote.
Under NAIC VM-20, principle-based reserving requires that prescribed, prudent-estimate, and stochastic assumptions each be documented, sourced to an experience study, and reconciled in the actuarial memorandum (VM-20 Section 9 for mortality, Section 7 for the reserve calculation). VM-21 extends the same discipline to variable annuity reserves. In Canada, OSFI E-23 mandates an enterprise-wide model risk management framework with independent validation (E-23 Principle 4 on validation), and LICAT ties capital adequacy directly to the quality of the underlying assumptions. IFRS 17 demands transparent discount-rate construction and an explicit risk adjustment, while US GAAP LDTI forces annual assumption unlocking with disclosed roll-forwards. Above all of these sits the model-risk expectation codified in SR 11-7: effective challenge, ongoing monitoring, and a documented validation trail for any model that informs financial reporting.
The cost of ignoring this is concrete. A single unreproducible validation run can invalidate an entire filing during examination; an undetected schema change in an upstream mortality feed can silently corrupt a quarter of reserves; a manual tolerance override with no audit record is precisely the finding that turns a routine exam into a remediation order. The rule engine described here exists to make those failure modes structurally impossible rather than merely unlikely. It is the assumption-side counterpart to the broader Regulatory Architecture & Compliance Mapping discipline and consumes the clean, typed data produced by Actuarial Model Ingestion & Testing Workflows.
Architecture Overview
An audit-ready validation architecture begins with strict separation of concerns. The projection engine must never embed validation logic; it consumes pre-validated, versioned assumption sets. This decoupling lets model changes, regulatory updates, and assumption overrides be tracked independently without contaminating core calculation logic. Every assumption carries immutable metadata — effective date, source authority, approval workflow, version hash, and explicit regulatory mapping — and each validation run flows through the same deterministic stages: schema parse, cryptographic fingerprint, rule evaluation, exception routing, and hand-off to the projection layer.
The rule engine operates as a deterministic state machine. Inputs are parsed against formal schemas, evaluated against business logic, and routed through exception handlers before reaching the projection layer. The engine must support idempotent execution: repeated runs with identical inputs yield byte-identical outputs. That property is non-negotiable for regulatory examinations, where auditors require reproducible validation trails without reliance on ephemeral environments or manual spreadsheet adjustments. The sections below walk each phase of this pipeline in turn.
Phase 1 — Schema Enforcement at the Ingestion Boundary
Validation starts by refusing to let malformed data enter the engine at all. Before any actuarial rule fires, an assumption payload must satisfy a formal contract: correct types, mandatory metadata, and structural invariants such as positive rates and monotonic age vectors. Enforcing this boundary with Schema Validation with Pydantic & Great Expectations converts an entire class of silent data-corruption failures into loud, early exceptions that never reach the reserve calculation.
from datetime import datetime
from typing import Any, Dict, List, Optional
from pydantic import BaseModel, Field, field_validator
class AssumptionMetadata(BaseModel):
"""Immutable provenance carried by every assumption set."""
assumption_id: str
valuation_date: datetime
source_authority: str
experience_study_ref: str
approval_workflow_id: str
regulatory_mapping: List[str] = Field(min_length=1) # e.g. ["VM-20 S9", "LICAT"]
version_hash: Optional[str] = None
class MortalityAssumption(BaseModel):
metadata: AssumptionMetadata
mortality_table: str # e.g. "2017 CSO ANB"
ages: List[int]
qx_rates: List[float]
improvement_scale: Optional[str] = None
tolerance_band: float = Field(ge=0.0, le=1.0)
@field_validator("qx_rates")
@classmethod
def rates_are_valid_probabilities(cls, v: List[float]) -> List[float]:
if not all(0.0 < q <= 1.0 for q in v):
raise ValueError("Every qx must lie in (0, 1]")
return v
@field_validator("qx_rates")
@classmethod
def rates_increase_with_age(cls, v: List[float]) -> List[float]:
# Base mortality must be non-decreasing across the modeled age band.
if any(later < earlier for earlier, later in zip(v, v[1:])):
raise ValueError("qx must be monotonic non-decreasing in age")
return v
Two invariants matter most here. First, every qx is a genuine probability in the half-open interval (0, 1]; a negative or zero mortality rate is not a warning, it is a rejection. Second, base mortality is monotonic in age — a violation almost always signals a table-loading error rather than a real demographic effect. Because these checks run before the engine assigns a fingerprint, a rejected payload never receives a version hash and can never be mistaken for validated input downstream.
Phase 2 — Deterministic Rule Evaluation and Fingerprinting
Once a payload is well-typed, the engine assigns it a cryptographic identity and evaluates it against the business rules. The fingerprint is the linchpin of reproducibility: it is computed from a canonical JSON serialization with sorted keys, so identical inputs always produce identical hashes regardless of dictionary ordering or environment. Rule evaluation then classifies the payload as PASS, WARN, or FAIL and emits a structured audit record.
import hashlib
import json
import logging
from datetime import datetime, timezone
logger = logging.getLogger("assumption_validator")
class ValidationResult(BaseModel):
status: str # "PASS" | "WARN" | "FAIL"
checksum: str
valuation_date: datetime
exceptions: List[str] = []
metadata_snapshot: Dict[str, Any]
def compute_deterministic_hash(payload: Dict[str, Any]) -> str:
"""SHA-256 over a canonical serialization for idempotent audit tracking."""
canonical_json = json.dumps(payload, sort_keys=True, default=str)
return hashlib.sha256(canonical_json.encode("utf-8")).hexdigest()
def validate_assumption(payload: MortalityAssumption) -> ValidationResult:
checksum = compute_deterministic_hash(payload.model_dump())
exceptions: List[str] = []
status = "PASS"
# Hard rule: mortality improvement must not exceed a plausible ceiling.
for age, qx in zip(payload.ages, payload.qx_rates):
if qx <= 0.0:
exceptions.append(f"Non-positive qx at age {age}")
status = "FAIL"
# Soft rule: an unusually tight tolerance band demands secondary review.
if status != "FAIL" and payload.tolerance_band < 0.05:
status = "WARN"
exceptions.append("Tolerance band below 5% requires secondary actuarial review")
result = ValidationResult(
status=status,
checksum=checksum,
valuation_date=payload.metadata.valuation_date,
exceptions=exceptions,
metadata_snapshot=payload.metadata.model_dump(),
)
logger.info(json.dumps(result.model_dump(), default=str))
return result
The separation between hard rules (which force a FAIL) and soft rules (which raise a WARN but let processing continue) is deliberate. Hard rules encode invariants that can never be overridden without a corrected input — a non-positive rate, a broken monotonicity constraint, a missing regulatory tag. Soft rules encode judgement thresholds where a qualified actuary may knowingly accept the result after review, provided that acceptance is itself logged. The compute_deterministic_hash function guarantees that identical inputs always yield identical checksums, satisfying the reproducibility requirement that examiners apply to model governance.
Phase 3 — Domain-Specific Validation Pipelines
Actuarial validation spans several interdependent domains, each with distinct statistical tests, regulatory mappings, and tolerance thresholds. The engine dispatches each assumption class to a specialized pipeline rather than forcing one generic rule set to cover them all.
Mortality and morbidity assumptions demand cohort analysis, credibility weighting, and reconciliation against industry tables such as the 2017 CSO or 2012 IAM. A robust pipeline flags deviations from expected improvement scales, applies credibility adjustments for sparse blocks, and documents the statistical basis for any override. The full methodology — including exposure-weighted metrics and the log-ratio drift check against published benchmarks — lives in Mortality & Morbidity Rate Validation, which embeds credibility thresholds directly into the rule evaluation layer.
Policyholder behavior introduces state-dependent complexity, particularly around lapse and surrender dynamics. Behavioral assumptions must be validated against historical persistency curves, macroeconomic drivers, and product features, with automated detection of structural breaks and enforcement of minimum regulatory floors. Those patterns — transition-matrix construction and behavioral override routing — are documented in Policy Lapse & Surrender Assumption Engines.
Economic assumptions require alignment with market-consistent yield curves, volatility surfaces, and inflation expectations. The validation layer verifies that scenario generators respect no-arbitrage conditions, that discount rates match the prescribed curve, and that correlation matrices remain positive semi-definite. Curve bootstrapping and scenario-consistency checks that satisfy both IFRS 17 and VM-20 are covered in Economic Scenario Mapping & Yield Curve Alignment, which in turn draws on the Stochastic Scenario Generation Frameworks used upstream. For portfolios with millions of policies, these pipelines run as vectorized batches — see Pandas & NumPy for Actuarial Data Pipelines and Async Batch Processing for Large Models for the execution substrate.
from typing import Callable
# A registry maps each assumption class to its domain pipeline. New classes are
# added by registration, never by editing a central conditional block.
PIPELINE_REGISTRY: Dict[str, Callable[[MortalityAssumption], ValidationResult]] = {}
def register(assumption_class: str):
def _wrap(fn: Callable[[MortalityAssumption], ValidationResult]):
PIPELINE_REGISTRY[assumption_class] = fn
return fn
return _wrap
@register("mortality")
def mortality_pipeline(payload: MortalityAssumption) -> ValidationResult:
return validate_assumption(payload)
def dispatch(assumption_class: str, payload: MortalityAssumption) -> ValidationResult:
if assumption_class not in PIPELINE_REGISTRY:
raise KeyError(f"No validation pipeline registered for '{assumption_class}'")
return PIPELINE_REGISTRY[assumption_class](payload)
The registry pattern keeps the engine open for extension and closed for modification: adding a new assumption class means registering a pipeline, never editing a monolithic conditional. That discipline is what lets a validation framework survive years of regulatory change without accreting unmaintainable branching logic.
Phase 4 — Exception Routing and Dynamic Threshold Management
Static tolerance thresholds become obsolete as market conditions shift and experience studies mature. A production engine implements adaptive thresholding that responds to assumption drift without compromising auditability. By tracking rolling error distributions and applying statistical process control, the framework adjusts warning bands automatically while holding hard fail thresholds fixed for material deviations. The mechanics — EWMA smoothing, CUSUM change detection, and Bayesian updating of the prudent estimate — are developed in Dynamic Threshold Tuning for Assumption Drift.
Drift is quantified with the Population Stability Index, which compares the distribution of an incoming assumption against its validated baseline across buckets:
where and are the actual and expected proportions in bucket . A common governance convention treats as stable, as a warning that triggers secondary review, and as a material shift that forces recalibration.
import numpy as np
def population_stability_index(expected: np.ndarray, actual: np.ndarray) -> float:
"""PSI between a validated baseline and an incoming assumption distribution."""
eps = 1e-6 # floor to avoid division-by-zero and log singularities
e = np.clip(expected, eps, None)
a = np.clip(actual, eps, None)
return float(np.sum((a - e) * np.log(a / e)))
def route_on_drift(psi: float, lapse_rate: float, regulatory_floor: float) -> str:
if lapse_rate < regulatory_floor:
return "FAIL" # hard floor is never soft-overridden
if psi >= 0.25:
return "FAIL" # material distributional shift
if psi >= 0.10:
return "WARN" # route to secondary actuarial review
return "PASS"
Data gaps are inevitable in actuarial workflows. When a primary source fails, the engine executes a deterministic fallback chain that preserves compliance posture: prescribed regulatory defaults first, then industry benchmarks, then a conservative in-house estimate — with each step explicitly logged and version-controlled so an examiner can see exactly when and why a default was substituted.
Phase 5 — Regulatory Mapping and Filing Automation
The ultimate purpose of the engine is to turn validated assumptions into compliant filing artifacts. Each assumption maps explicitly to a regulatory requirement: VM-20 requires documentation of prescribed and stochastic assumptions, IFRS 17 demands a transparent discount-rate and risk-adjustment methodology, and LICAT enforces capital-adequacy stress parameters. By embedding regulatory tags in the metadata schema, the engine generates compliance matrices, traceability reports, and substantiation packages automatically. Filing-format specifics — the VM-20 actuarial memorandum structure and OSFI attestation forms — are handled in NAIC VM-20 Compliance Frameworks and OSFI Model Risk Management Guidelines.
def build_substantiation_row(result: ValidationResult) -> Dict[str, str]:
"""Flatten one validation result into a filing-package traceability row."""
meta = result.metadata_snapshot
return {
"assumption_id": meta["assumption_id"],
"valuation_date": result.valuation_date.date().isoformat(),
"regulatory_mapping": "; ".join(meta["regulatory_mapping"]),
"experience_study_ref": meta["experience_study_ref"],
"status": result.status,
"checksum": result.checksum, # ties the filing to an exact input
}
Because every row carries the SHA-256 checksum of the exact input that produced it, the filing package is self-verifying: a reviewer can recompute the hash from the archived assumption set and confirm that the number in the memorandum came from that input and no other. This eliminates manual reconciliation and reduces examination preparation from weeks to hours.
Assumption Governance
Governance is the discipline that decides which assumptions enter the engine in the first place and how they are justified. Mortality, lapse, and interest assumptions are each selected through a documented chain: an experience study establishes observed rates, a credibility procedure blends that experience with an industry table, and a prudent-estimate margin is added where the framework requires it. Every step is recorded in the experience_study_ref and source_authority fields so that the provenance of a rate is inseparable from the rate itself.
The economic consequence of a validated assumption set is the reserve it produces. For a fully discrete prospective policy, the reserve at duration is the expected present value of future benefits net of future net premiums:
where is the discount factor, is the survival probability drawn from the validated mortality table, and is the one-year mortality rate. A validated lapse assumption enters through the survival and persistency factors; a validated interest assumption enters through . Because the reserve is a direct function of these inputs, an unvalidated assumption is an unquantified error in the balance sheet. The engine therefore treats governance metadata as load-bearing: a payload missing its experience-study reference fails the schema contract in Phase 1 and never reaches the reserve calculation. The domain guides on mortality, lapse, and economic assumptions each document their governance procedures in full.
Regulatory Audit Trail Requirements
A validation trail is only useful if it is tamper-evident and complete. Three properties define an examiner-ready trail. First, cryptographic data lineage: every assumption set, every intermediate result, and every filing row is bound to a SHA-256 fingerprint, and results are chained so that altering an earlier record invalidates every later one. Second, immutable, append-only logs: validation events are written once to write-once storage, never updated in place, so the history of what was validated and when cannot be silently rewritten. Third, a reconstructable package: given a valuation date, the trail must reproduce the exact assumption set, rule version, and threshold configuration that produced a filing, without depending on a running environment.
These requirements are the assumption-side application of the patterns in Actuarial Audit Trail Architecture — hash-chained logs, WORM storage, and examiner-package assembly. When assumption sets contain policyholder detail, the trail must also respect the controls in Data Security & PII Boundaries for Filing Systems, keeping identifying data outside the artifacts that leave the trust boundary. Structured, machine-readable logs let compliance teams export validation trails directly into submission formats, so the same records that satisfy internal model-risk committees also satisfy external examiners.
Failure Modes and Operational Risk
A validation engine that works in development can still fail catastrophically in production. The failure modes below are the ones that most often turn into filing incidents, each paired with its mitigation.
- Seed non-determinism. Stochastic components seeded from wall-clock time produce different scenario sets on re-run, breaking reproducibility. Mitigation: pin and record every random seed in the assumption metadata, and assert byte-identical output in a regression test against a stored baseline.
- Schema drift in upstream feeds. A vendor silently adds a column or changes a rate’s units, and a permissive parser accepts it. Mitigation: enforce the Phase 1 contract strictly, fail closed on unknown fields, and alert on any change to the input schema hash.
- Memory exhaustion on large portfolios. Loading a multi-million-policy block into memory for validation exceeds available RAM and the process is killed mid-run, leaving a partial trail. Mitigation: stream validation in bounded batches and checkpoint the fingerprint chain so a killed run resumes without gaps — see the async batch-processing patterns referenced above.
- Silent tolerance overrides. An operator relaxes a threshold to clear a
WARNand forgets to record why. Mitigation: make every override a logged, attributed event; an override with no justification is itself a hardFAIL. - Filing-deadline misses from serial processing. A single-threaded pipeline cannot finish before a quarter-end cutoff. Mitigation: parallelize independent assumption classes through the pipeline registry and monitor throughput against the deadline with alerting to the governance committee.
Monitoring dashboards should track validation pass rates, exception frequencies, and drift metrics in real time, with alert thresholds calibrated to notify governance committees before a material deviation reaches a capital or pricing model. Treating validation as a continuous automated process rather than a periodic manual exercise is what converts these risks from incidents into non-events.
Compliance Mapping
The table below maps each governing framework to the pipeline component that satisfies it and the concrete implementation artifact an examiner can inspect.
| Regulation | Pipeline component | Implementation artifact |
|---|---|---|
| NAIC VM-20 Section 9 (mortality) | Phase 3 mortality pipeline | Credibility-adjusted table with experience-study reference and checksum |
| NAIC VM-20 Section 7 (reserve calc) | Phase 5 filing automation | Substantiation rows tying each reserve input to a SHA-256 fingerprint |
| NAIC VM-21 (VA reserves) | Phase 3 economic + behavioral pipelines | Scenario-consistency and lapse-floor validation results |
| OSFI E-23 Principle 4 (validation) | Phases 1–2 schema + rule engine | Deterministic rule versions and independent-validation log |
| OSFI LICAT | Phase 4 threshold management | Stress-parameter checks and drift (PSI) monitoring records |
| IFRS 17 | Phase 3 economic pipeline | Discount-curve and risk-adjustment validation trail |
| US GAAP LDTI | Phase 4 assumption unlocking | Annual roll-forward with attributed override log |
| SR 11-7 | Audit-trail layer | Hash-chained, append-only validation records and examiner package |
The move to code-first assumption validation is a strategic imperative. An engine that enforces deterministic execution, cryptographic audit trails, and adaptive thresholding converts actuarial governance from a compliance burden into a durable operational advantage — and as regulatory expectations keep rising, the organizations that invest in production-grade validation lead on transparency, efficiency, and capital optimization.
Related
- Mortality & Morbidity Rate Validation — cohort analysis, credibility weighting, and drift checks against industry tables.
- Policy Lapse & Surrender Assumption Engines — state-dependent behavioral validation and override routing.
- Economic Scenario Mapping & Yield Curve Alignment — no-arbitrage and discount-curve validation.
- Dynamic Threshold Tuning for Assumption Drift — EWMA, CUSUM, and Bayesian threshold adaptation.
- Regulatory Architecture & Compliance Mapping — the filing and audit-trail context this engine feeds.
Up a level: Actuarial Validation & Filing Automation — the home of the model ingestion, assumption validation, and regulatory filing tracks.