Tier 3 · Internal Strategy

Industry Rating Model (Prism Score)

M-200 · lifecycle: monitoring · RAT-200-v1.0.1

Intended Use

Industry Rating Model (Prism Score) Assign comparable financial-strength scores to insurance firms using public filings (CapScore subproject — replaces NRSRO ratings).

Public-data-driven analogue to AM Best / S&P / Moody's NRSRO ratings. Input: statutory + GAAP filings via FinView and StatSight. Output: Prism Score (firm-level financial strength index). Per Decision 044 the rating is implied purely from fundamentals — the engine ingests no agency rating (disclosed ratings are validation targets, not inputs). Active at Tier-3 (RAT-200-v1.0.1); factor weights/thresholds remain approximated from public agency methodology, not calibrated against a labeled rating dataset (COND-003 open — gates external publication or promotion above Tier-3; D044 implied-vs-disclosed cross-check INV-026 is the open build).


Components

Inputs, processing, outputs

data sources
DS-008 · DS-021
assumptions
A-080, A-081
engines
insmodel.L3.rating_model
contracts
rating_results_v1
dimensions
D3

Methodology & Mechanics

Methodology

Identity note (INV-031, reconciled — RAT-200 COND-001). The backing engine is keyed by its dotted engine_id insmodel.L3.rating_model, not by a model_id. Its legacy Scheme-B governance id M-916 is RETIRED per Decision 046: the rating_model.py source header now reads "Engine ID: insmodel.L3.rating_model | Model membership: M-200 (Tier-3)", the engine_registry no longer carries an active model_id, and the legacy_metadata archive marks M-916 RETIRED. The Scheme-A linkage is model_membership: [M-200], and this card + the model registry use M-200 (intended-use model scheme). The historical M-916 string survives only in the SR 11-7 archive lineage; it is not an active identity.

M-200 (engine insmodel.L3.rating_model, class RatingModel v1.0.0) is an implied financial-strength / credit rating model. It takes a single firm's disclosed fundamental metrics and produces a model-implied AM Best and S&P rating, a 0–100 composite ("Prism") score, ten factor sub-scores, per-factor weighted contributions, a peer percentile, and a distance-to-downgrade buffer.

The score is built in six mechanical steps (RatingModel.calculate, firmmodel/engines/rating_model.py):

  1. KPI extraction. The 15 optional fundamental KPIs declared in ENGINE_CONTRACT["inputs"]["optional"] are read per firm from the input DataFrame: rbc_ratio_est, asset_leverage, debt_equity, investment_yield, loss_ratio, expense_ratio, roe, operating_leverage, reserve_coverage, reinsurance_leverage, interest_coverage, duration_gap_proxy, credit_quality_score, liquidity_coverage_ratio, total_assets_B. The only required input is a firm identifier (firm_name|firm_id); every KPI is optional and missing KPIs are tolerated — see step 2. (Note: not all 15 declared KPIs map to a scoring factor; the 10 factors in step 2 consume a subset, with ig_allocation_est / equity_allocation_pct used as factor alternates.)
  2. Per-factor scoring. Each of 10 factors maps one (or two) KPIs to a 0–100 sub-score by piecewise-linear interpolation against calibrated threshold breakpoints (_interpolate_score). For example capitalization maps RBC 400% → 100, 300% → 80, 250% → 60, 200% → 40, 150% → 20. Factors carrying a primary and an alternate KPI (e.g. operating_performance = ROE + investment yield) blend them 70/30 when both are present. A factor whose KPI is absent receives a neutral score of 50.0 (NEUTRAL_SCORE) rather than being dropped.
  3. Composite. The composite Prism score is the weighted sum of the ten sub-scores, with weights summing to 1.0 (RATING_FACTORS, firmmodel/config/rating_factors.py). Capitalization (0.25) and operating performance (0.20) dominate; liquidity (0.10) is next.
  4. Rating mapping. The composite is mapped to an implied AM Best / S&P band via a top-down threshold table (RATING_THRESHOLDS, _map_composite_to_ratings): ≥90 → A++/AAA, ≥80 → A+/AA, ≥70 → A/A, ≥60 → A-/BBB+, ≥50 → B++/BBB, ≥40 → B+/BB, else B/B.
  5. Peer percentile. When more than one firm is scored together, each firm's composite is ranked against the others in the input population.
  6. Distance to downgrade. The number of composite points the firm sits above the next lower rating threshold (_compute_distance_to_downgrade), a thin "buffer" diagnostic — not a probability.

The D044 stance: recover, don't ingest

Per Decision 044, the platform's posture is recover, don't ingest. Agency ratings (AM Best, S&P, Moody's) are treated strictly as outputs that the model is validated against — never as model inputs. M-200 implies a rating purely from disclosed fundamentals; it never reads a disclosed agency letter grade into the scoring path. Inspect the input contract (ENGINE_CONTRACT["inputs"]) and you will find only fundamental KPIs — no agency_rating field exists. The disagreement between the model-implied rating and a firm's actually-disclosed agency rating is the product signal: where the fundamentals imply A++ but the agency says A-, that gap is the analyst's lead, not an error to be calibrated away. Agency methodology documents (AM Best BCAR, S&P, Moody's) inform the factor thresholds, but the agencies' published outputs enter only as a downstream cross-check.


Key Assumptions

Key Assumptions and Their Justification

The model's behavior is governed almost entirely by the ten factor weights and their threshold tables. Weights are approximated from public agency methodology descriptions and sum to 1.0.

Factor Weight Primary KPI (alt) Direction Justification
capitalization 0.25 rbc_ratio_est higher better Capital adequacy is the single largest driver in AM Best BCAR and S&P capital models; RBC ratio is the most load-bearing solvency signal.
operating_performance 0.20 roe (+ investment_yield) higher better Earnings power and investment yield are the primary forward-looking strength indicators; blended 70/30 to balance accounting ROE against asset productivity.
liquidity 0.10 liquidity_coverage_ratio higher better Liquidity stress is the proximate cause of insurer failure even when solvent on a book basis; weighted above most individual risk factors.
fixed_income_risk 0.08 credit_quality_score (+ ig_allocation_est) higher better Asset credit quality dominates the general-account risk profile of life insurers.
credit_risk 0.08 reinsurance_leverage lower better Heavy reliance on ceded reserves introduces counterparty exposure; high reinsurance leverage is penalized.
reserve_risk 0.08 reserve_coverage higher better Reserve adequacy (held / required) directly bounds balance-sheet integrity.
ir_duration_risk 0.06 duration_gap_proxy lower better Asset-liability duration mismatch drives rate-shock vulnerability; small absolute gap is rewarded.
premium_risk 0.06 operating_leverage lower better Net-premium-to-surplus leverage; high writing leverage thins the capital cushion.
business_risk 0.05 expense_ratio (+ net_income_margin) lower better Operational efficiency proxy; high expense ratios erode margin.
equity_risk 0.04 equity_allocation_pct lower better Equity allocation in the general account adds capital volatility; smallest weight reflects modest typical exposure.

Prose justification. The weighting deliberately concentrates ~45% of the score in capitalization + operating performance, mirroring the public emphasis of AM Best and S&P that solvency and earnings dominate insurer financial strength. The remaining 55% is spread across asset, reserve, reinsurance, duration, premium-leverage, efficiency, liquidity, and equity-risk factors so that no single secondary factor can swing the composite by more than ~10 points. Two operational assumptions matter materially: (a) missing KPIs are scored 50, not excluded — so a firm disclosing only its name lands exactly at composite 50 (B++/BBB), the deliberate "no information → mid-band" prior; and (b) interpolation is linear between breakpoints, whereas real agency mappings are often non-linear — a known approximation.


Output Snapshot

Output Snapshot

Deterministic three-firm run of RatingModel v1.0.0 — reproducible, requires no live firm data (python scripts/model_snapshots.py M-200 in InsModel; the same canonical strong-firm fundamentals are asserted by tests/test_rating_model.py::TestRatingModelCore). The StrongCo input is the test's _strong_firm_row(); MidCo and WeakCo are included only to illustrate peer ranking and the rating spread.

Input (StrongCo, synthetic): RBC 400% · ROE 12% · investment yield 5% · reserve coverage 1.20 · expense ratio 18% · operating leverage 1.5 · reinsurance leverage 0.15 · credit-quality 0.93 · liquidity coverage 1.8 · duration gap 0.8 · equity allocation 3%.

output value meaning
composite_score (StrongCo) 92.17 weighted Prism score (0–100)
implied_am_best A++ implied AM Best band (composite ≥ 90)
implied_sp AAA implied S&P band
peer_percentile 100.0 top of the 3-firm input population
distance_to_downgrade 2.17 composite points above the A++ (90) floor

Factor contributions (score × weight → contribution to 92.17):

factor sub-score weight contribution
capitalization 100.00 0.25 25.00
operating_performance 83.00 0.20 16.60
liquidity 92.00 0.10 9.20
reserve_risk 100.00 0.08 8.00
fixed_income_risk 92.00 0.08 7.36
credit_risk 90.00 0.08 7.20
premium_risk 90.00 0.06 5.40
ir_duration_risk 88.00 0.06 5.28
business_risk 88.00 0.05 4.40
equity_risk 93.33 0.04 3.73

Peer rating spread (implied AM Best / S&P / composite): StrongCo A++/AAA/92.17 · MidCo A+/AA/82.37 · WeakCo B/B/23.78.

The contributions sum exactly to the composite (25.00 + 16.60 + 9.20 + 8.00 + 7.36 + 7.20 + 5.40 + 5.28 + 4.40 + 3.73 = 92.17), confirming the weighted-sum construction. Capitalization alone contributes 25 of the 92 points; together with operating performance it accounts for 42 of the 92. The distance-to- downgrade of 2.17 shows StrongCo sits only ~2 composite points above the A++ floor — a thin buffer despite the strong inputs, because the A++ band starts at 90. No disclosed agency rating entered this computation (D044): if this firm's actual disclosed rating were, say, A+ rather than A++, that one-notch gap would be the reported signal.

Captured 2026-06-04 · deterministic, no live data.


Limitations

Limitations and Known Gaps

  1. Indicative-only — not production-validated. M-200 is active at Tier-3 for internal strategy use (RAT-200-v1.0.1, 2026-06-07) but is NOT production-validated: the factor weights and thresholds are approximated from public agency methodology descriptions, not calibrated against a labeled rating dataset (COND-003, open — gates external publication or promotion above Tier-3). Implied ratings are indicative only and must not be relied on as financial-strength opinions.
  2. Weights and thresholds are approximations of proprietary methodologies. Actual AM Best / S&P / Moody's weights and mappings are proprietary and non-linear; M-200 uses linear interpolation between public breakpoints. Calibration error against the true agency surfaces is unquantified.
  3. No qualitative overlay. Management quality, enterprise risk management, strategic positioning, group support, and sovereign/sector ceilings — all material to real agency ratings — are entirely absent. The model is purely quantitative.
  4. Missing-KPI neutral-score bias. Absent KPIs are scored 50, not excluded. A sparsely-disclosing firm is pulled toward the B++/BBB mid-band regardless of its true posture; a firm disclosing only a name scores exactly
  5. This can mask both strength and weakness.
  6. Peer percentile is population-relative, not universe-relative. The percentile ranks only firms in the input DataFrame (here, 3), not the full rated universe; the 100.0 shown is "best of these three," not "100th percentile of all insurers."
  7. Single-period snapshot; no outlook or trajectory. The model captures one point in time. It produces no rating outlook, no transition probability, and no through-the-cycle adjustment; thresholds are static across economic regimes.
  8. Distance-to-downgrade is a static buffer, not a probability. It assumes all other factors hold constant (ceteris paribus) and measures only points above the next threshold; it is not a calibrated downgrade likelihood.
  9. No holding-company notching. Subordinated-debt or holdco-structure notching that agencies apply is not modeled.
  10. Subject to BV-032. Snapshot figures here are deterministic on synthetic fundamentals; any run on live firm data is exposed to the BV-032 firm-data divergence and is out of scope for this card.

Tracked for ratification. Status after the RAT-200 remediation pass: - Engine identity + tier (COND-001) — RESOLVED. The engine_registry tier inconsistency is fixed: insmodel.L3.rating_model is now tier: tier-3 / owner: engine-owner-intelligence (was tier-1 / engine-owner-actuarial), matching the Tier-3 M-200 it solely feeds, per R-12 tier inheritance; the legacy Scheme-B model_id: M-916 is retired across source header, engine_registry, and legacy_metadata (see the Identity note under Methodology). - Registry documentation_pack (COND-002) — RESOLVED. model_card flag set to present in model_registry.yaml. - Scaffold maturity / D044 cross-check (COND-003) — OPEN (non-blocking at Tier-3). Output-changing / modeling-code items left for a future build: the D044 implied-vs-disclosed agency-rating cross-check (INV-026 — the model-vs-agency disagreement is the product signal; recorded in the Validation Packet as the intended next step, not yet implemented), and a calibrated validation-evidence pack (the ten factor weights/thresholds are approximated from public agency methodology, not calibrated against a labeled dataset). These MUST close before external publication or promotion above Tier-3.


Validation Evidence

Validation Packet

Check Where What it asserts
Weights sum to 1.0 test_rating_model.py::test_factor_weights_sum_to_one The 10 RATING_FACTORS weights sum to 1.0 within 1e-9.
Composite in [0,100] ::test_composite_score_range Every firm's composite is bounded to [0, 100].
Strong firm rates well ::test_high_quality_firm_rates_well RBC 400% / ROE 12% / reserve-cov 1.20 → A+ or A++ (the canonical input above yields A++).
Weak firm rates poorly ::test_weak_firm_rates_poorly RBC 120% / ROE 2% / reserve-cov 0.90 → B+ or B (WeakCo → B).
Threshold boundaries ::test_rbc_scoring_boundaries RBC 400/300/250/150% interpolate to ~100/80/60/20.
Missing KPI → neutral 50 ::test_missing_kpi_gets_neutral_score Name-only firm scores composite ≈ 50.0.
Directionality ::test_higher_is_better_direction, ::test_lower_is_better_direction higher-is-better and lower-is-better factors move the score in the correct direction.
Rating mapping ::test_score_90_maps_to_a_plus_plus, ::test_score_50_maps_to_b_plus_plus Composite 92 → A++/AAA; 55 → B++/BBB.
Monotonic mapping ::test_score_ordering Across composite 0→100 in steps of 5, the implied rating never gets worse as the score rises.
Peer ranking ::test_multi_firm_peer_ranking Three firms get distinct percentiles in [0,100]; strongest > weakest.
Distance-to-downgrade ::test_distance_to_downgrade_computed, ::test_strong_firm_has_positive_buffer Buffer column present, non-NaN, and positive for a strong firm.

Suite status: 15 passed (pytest tests/test_rating_model.py, re-run 2026-06-12). Ratified active at Tier-3 by RAT-200-v1.0.1 (2026-06-07); the standing evidence is the abbreviated Tier-3 pack at modelling/validation_evidence/M-200/v1.0.0/ (the unit suite + the deterministic snapshot — no calibrated validation exists). Recover-don't-ingest validation (D044) is the intended next step — cross-checking implied ratings against disclosed agency ratings as targets, which is not yet implemented (INV-026, open). The curated disclosed-rating validation-target tables located in CapScore/sources/*_scraper.py (provenance: public agency lookups/press releases, 14 tickers) are the identified target data for that build; the open gap is the per-firm 15-KPI fundamentals vectors (FinView/StatSight assembly).


References

References

Internal: - Decision 044 (recover-don't-ingest) — agency ratings are validation targets/outputs, never model inputs; the model-vs-agency disagreement is the signal. - Engine source: ecosystem/InsModel/Models/firmmodel/engines/rating_model.py (class RatingModel, insmodel.L3.rating_model). - Factor config: ecosystem/InsModel/Models/firmmodel/config/rating_factors.py (RATING_FACTORS, RATING_THRESHOLDS). - Tests: ecosystem/InsModel/Models/tests/test_rating_model.py. - Snapshot: ecosystem/InsModel/Models/scripts/model_snapshots.py (snap_M_200). - Legacy metadata: firmmodel/governance/legacy_metadata/RatingModel.yaml.

Agency methodology (comparison / threshold provenance — outputs validated against, not ingested): - AM Best — Best's Capital Adequacy Ratio (BCAR) methodology (public). - S&P Global — Insurance Rating Criteria (insurer financial strength framework). - Moody's — Financial Metrics and Scorecard for Insurance Companies. - NAIC — Insurance Regulatory Information System (IRIS) ratio benchmarks.


Change Log

Change Log

Card change history. Code-side change history lives in git log of the component files.

  • 2026-05-08 — stub created from registry data per Decision 023 Phase 5 / B-07.
  • 2026-06-06 — code-grounded documentation-accuracy pass against firmmodel/engines/rating_model.py ENGINE_CONTRACT + engine_registry M-200 entry. Removed the stale, self-contradictory "NRSRO ratings hardcoded pending live extraction" phrasing from the Description and replaced it with the code-verified D044 stance (engine ingests no agency rating; rating is implied from fundamentals) plus an honest scaffold-maturity statement. Added Standards Coverage (ASOP_56 sole conformance regime + AM Best / S&P / Moody's / NAIC IRIS threshold provenance, validated-against not ingested per D044) and Dependencies (upstream models: none, grounded in registry — empty-by-design vs omitted) sections, mirroring the M-001 structure. Completed Methodology step-1 KPI enumeration to the full 15 ENGINE_CONTRACT optional inputs. Added a dual-ID note (engine governance id M-916 vs card/registry M-200 — the known INV-031 overload). Noted gated/ratification items (INV-026 D044 cross-check, ratification + validation evidence, engine_registry tier-1 inconsistency under R-12) under Limitations. No model outputs, back-test numbers, or validation results were fabricated or changed.
  • 2026-06-06 — RAT-200 remediation pass (1L, Decision 053). Closed COND-001 (engine identity + tier collision): retired the legacy Scheme-B model_id: M-916 across the rating_model.py source header, the engine_registry, and the legacy_metadata archive per Decision 046, and re-tagged the engine tier: tier-3 / owner: engine-owner-intelligence to match the Tier-3 M-200 it solely feeds (R-12 tier inheritance). Rewrote the Methodology Identity note and the Limitations ratification block to reflect the resolution. Closed COND-002: set documentation_pack.model_card: present in model_registry.yaml. Assembled the Tier-3 validation-evidence pack at modelling/validation_evidence/M-200/v1.0.0/ from the existing 15/15 unit suite + the deterministic snapshot reconciliation (no new numbers generated). COND-003 (scaffold maturity / D044 INV-026 cross-check / uncalibrated weights) remains honestly open — a genuine engine-build that is not fabricated. No model outputs, back-test numbers, or validation results were fabricated or changed.
  • 2026-06-12 — RAT-200-v1.0.1 post-ratification reconciliation pass (1L, Decision 053 remediation flow for INV-026 + COND-003). Doc reconciliation: card status under_developmentactive (RAT-200-v1.0.1 promoted M-200 on 2026-06-07; the registry was already reconciled), Limitation 1 and the validation-suite paragraph updated to the post-ratification reality, the model_registry.yaml description's stale "scaffold maturity / NRSRO ratings hardcoded pending live extraction" phrasing replaced with the code-verified D044 stance, and the model_snapshots.py M-200 header re-labeled ACTIVE/Tier-3 (figures unchanged — re-run 2026-06-12 reproduces composite 92.17 / A++ / AAA exactly; unit suite 15/15). F-001 closed: legacy_metadata index.yaml RatingModel entry annotated M-916 RETIRED (mirrors the LiquidityEngine/RAT-053 precedent). INV-026 and COND-003 remain HONESTLY OPEN — genuine engine/data builds (no cross-check or calibration result was fabricated). Scoping addendum added at modelling/validation_evidence/M-200/v1.0.0/addendum-2026-06-12-inv026-cond003-scoping.md: the disclosed-rating validation-target data EXISTS (curated public-lookup tables in CapScore/sources/*_scraper.py, 14 tickers); the remaining build is the per-firm 15-KPI fundamentals assembly + the cross-check harness, then weight/threshold calibration. No model outputs, back-test numbers, or validation results were fabricated or changed.

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-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.

LOW INV-026 · P1 · capability-gap

Prism rating model under_development; D044 cross-check not implemented

RatingModel is functional (fundamentals -> implied score, no agency-rating input, so the D044 'recover-don't-ingest' stance is structurally enforced) but status under_development with no governance ratification and only a unit-test suite. The D044 next step — cross- checking implied vs disclosed agency ratings as validation targets — is not built.

Recommendation: Implement the implied-vs-disclosed cross-check (the disagreement is the signal, per D044), add validation evidence, and ratify before promoting out of under_development.


Ratification

Ratified — RAT-200-v1.0.1

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