Skip to content
This repository was archived by the owner on Mar 13, 2026. It is now read-only.

burning-cost/insurance-mrm

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

insurance-mrm

Model risk management framework for insurance pricing models.

The problem

Most UK insurers govern their pricing models with Word documents and Excel registers. A validation report is produced once at model launch, filed away, and referenced occasionally. Monitoring evidence (if it exists) lives in a separate tracker maintained by the pricing team. There is no audit trail connecting monitoring outputs to governance sign-offs.

This is not a criticism of any specific firm — it is a description of current practice across the market. The PRA's 2026 supervision priorities letter called out "gaps between assumed and realised profitability" for general insurers. PRA SS1/23 is expected to extend to insurers by 2026-2027. The current documentation approach will not survive a PRA model risk review.

insurance-mrm is the governance layer that sits on top of statistical validation and monitoring. It does not run statistical tests. Those are handled by insurance-validation (point-in-time validation reports) and insurance-monitoring (ongoing operational monitoring). What insurance-mrm provides is:

  • A model card format with the fields a Model Risk Committee actually needs: assumptions register with risk ratings per assumption, explicit not-intended-for list, monitoring plan with named owner and trigger thresholds
  • An objective risk tier scoring engine (0-100 composite score across 6 dimensions) that produces a defensible tier assignment with a verbose rationale string — not a judgment call
  • A persistent model inventory backed by a plain JSON file, queryable by tier, status, owner, and review due date
  • An executive committee report (HTML and JSON) that pulls together model identity, tier, validation status, monitoring summary, material assumptions, outstanding issues, and sign-off chain into a 2-3 page governance pack

Installation

pip install insurance-mrm

No mandatory dependencies beyond the standard library. Optional: insurance-validation and insurance-monitoring for richer integration.

Quickstart

from insurance_mrm import (
    ModelCard, Assumption, Limitation,
    ModelInventory, RiskTierScorer, GovernanceReport,
)

# 1. Build a model card
card = ModelCard(
    model_id='motor-freq-tppd-v2',
    model_name='Motor TPPD Frequency',
    version='2.1.0',
    model_class='pricing',
    intended_use='Frequency pricing for private motor. Not for commercial motor, fleet, or reserving.',
    not_intended_for=['Commercial motor', 'Fleet', 'Reserving', 'Capital'],
    target_variable='claim_count',
    distribution_family='Poisson',
    model_type='CatBoost',
    rating_factors=['driver_age', 'vehicle_age', 'annual_mileage', 'region'],
    training_data_period=('2019-01-01', '2023-12-31'),
    developer='Pricing Team',
    champion_challenger_status='champion',
    gwp_impacted=125_000_000,
    customer_facing=True,
    regulatory_use=False,
    assumptions=[
        Assumption(
            description='Claim frequency stationarity since 2022',
            risk='MEDIUM',
            mitigation='Quarterly A/E monitoring, PSI alert at >0.25',
        ),
        Assumption(
            description='Region as proxy for road density and theft risk',
            risk='LOW',
        ),
    ],
    limitations=[
        Limitation(
            description='Performance degrades for vehicles >10 years (thin training data)',
            impact='Higher prediction variance for this segment',
            population_at_risk='~8% of book',
        ),
    ],
    outstanding_issues=['VIF check on driver_age x annual_mileage pending'],
    approved_by=['Chief Actuary', 'Model Risk Committee'],
    approval_date='2024-10-15',
    approval_conditions='Subject to quarterly A/E monitoring',
    next_review_date='2025-10-15',
    monitoring_owner='Sarah Ahmed, Head of Pricing Analytics',
    monitoring_frequency='Quarterly',
    monitoring_triggers={'psi_score': 0.25, 'ae_ratio_deviation': 0.10, 'gini_drop_pct': 0.05},
)

# 2. Score the risk tier
scorer = RiskTierScorer()
tier = scorer.score(
    gwp_impacted=125_000_000,
    model_complexity='high',          # 'low' | 'medium' | 'high'
    deployment_status='champion',     # 'champion' | 'challenger' | 'shadow' | 'development' | 'retired'
    regulatory_use=False,
    external_data=False,
    customer_facing=True,
    validation_months_ago=6.0,
    drift_triggers_last_year=0,
)
print(f"Tier {tier.tier} ({tier.tier_label}): {tier.score:.1f}/100")
print(tier.rationale)

# 3. Register in the inventory
inventory = ModelInventory('mrm_registry.json')
inventory.register(card, tier)

due = inventory.due_for_review(within_days=60)
for model in due:
    print(f"{model['model_name']} — review due {model['next_review_date']}")

# 4. Generate the governance pack
report = GovernanceReport(
    card=card,
    tier=tier,
    validation_results={
        'overall_rag': 'GREEN',
        'run_id': 'a1b2c3d4-uuid',
        'run_date': '2024-10-01',
        'gini': 0.42,
        'ae_ratio': 1.01,
        'psi_score': 0.07,
    },
    monitoring_results={
        'period': '2025-Q3',
        'ae_ratio': 1.02,
        'psi_score': 0.06,
        'recommendation': 'Continue',
    },
)
report.save_html('motor_freq_mrm_pack.html')

Risk tier scoring

The scorer produces a 0-100 composite score across 6 dimensions. Weights sum to 100 and are configurable.

Dimension What it measures Max pts (default)
Materiality GWP influenced by the model 25
Complexity Model architecture and feature count 20
Data quality Use of external data sources 10
Validation coverage Months since last independent validation 10
Drift history Monitoring trigger events in last 12 months 10
Regulatory exposure Production status + regulatory use + customer-facing pricing 25

Tier thresholds (defaults):

Score Tier Label Review Sign-off
>= 60 1 Critical Annual Model Risk Committee
30-59 2 High 18 months Chief Actuary
< 30 3 Medium 24 months Head of Pricing

Override weights and thresholds at construction:

scorer = RiskTierScorer(
    weights={
        'materiality': 35, 'complexity': 20, 'data_quality': 5,
        'validation_coverage': 15, 'drift_history': 10, 'regulatory_exposure': 15,
    },
    thresholds={1: 65, 2: 35, 3: 0},
)

Model inventory

inventory = ModelInventory('mrm_registry.json')

# Register
inventory.register(card, tier)

# Query
rows = inventory.list()
rows = inventory.list(status='champion')
rows = inventory.list(tier=1)
rows = inventory.list(owner='Sarah')
due = inventory.due_for_review(within_days=60)
overdue = inventory.overdue()

# Update after validation
inventory.update_validation(
    model_id='motor-freq-tppd-v2',
    validation_date='2025-10-01',
    overall_rag='GREEN',
    next_review_date='2026-10-01',
    run_id='uuid-from-insurance-validation',
)

# Audit log
inventory.log_event(
    model_id='motor-freq-tppd-v2',
    event_type='monitoring_trigger',
    description='PSI exceeded 0.25 on driver_age feature',
)

print(inventory.summary())

The inventory file is plain JSON and git-auditable. Treat it as source-controlled documentation.

Regulatory context

Designed with PRA SS1/23 Principles 1 and 5 in mind. SS1/23 is currently scoped to banks but is expected to extend to insurers by 2026-2027. The tier scoring rubric and model card fields align with SS1/23 expectations.

FCA Consumer Duty (PRIN 2A.9) requires firms to regularly evidence customer outcomes. The monitoring plan fields and trigger thresholds on the model card support this documentation requirement.

Part of the Burning Cost ecosystem

insurance-mrm is library 28 in the Burning Cost portfolio. It is the governance wrapper for:

  • insurance-validation — point-in-time technical validation reports
  • insurance-monitoring — ongoing operational monitoring

A team using all three has a complete, auditable, PRA-aligned pricing model governance workflow in Python.

Performance

No formal benchmark. This is a governance library — computational cost is not the relevant metric. All operations are in-memory JSON manipulation and string rendering.

Operation Time
RiskTierScorer.score() < 1ms
ModelInventory.register() < 10ms
ModelInventory.list() — 100 models < 50ms
GovernanceReport HTML generation < 500ms
GovernanceReport JSON generation < 100ms

The operational cost that matters is the time a senior actuary spends assembling the MRC pack. Before this library: 2–4 hours per model (pulling validation results, updating the Excel scorecard, drafting the Word pack). After: 5–10 minutes (update the ModelCard fields, run GovernanceReport). The library's purpose is to make the governance workflow repeatable and auditable, not to run faster than a spreadsheet.

Licence

MIT

About

Model risk management — ModelCard, ModelInventory, RiskTierScorer, GovernanceReport

Topics

Resources

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages