Skip to main content

Dynamic insurance pricing models: GAS score-driven filters and Bayesian change-point detection

Project description

insurance-dynamics

Dynamic insurance pricing models for UK pricing teams.

Two problems, one package: tracking how your loss ratios are changing right now (GAS), and detecting when they shifted structurally in the past (changepoint).

Merged from: insurance-gas (GAS score-driven models) and insurance-changepoint (BOCPD/PELT detection).

The problems this solves

GAS models (insurance_dynamics.gas): Your GLM gives you a static relativity. Reality gives you frequency that drifts quarter by quarter. GAS (Generalised Autoregressive Score) models fit a time-varying parameter model to claim counts, severities, or loss ratios — producing a filter path that shows you how the underlying rate has moved, with confidence bands from parametric bootstrap.

Changepoint detection (insurance_dynamics.changepoint): You suspect something changed — whiplash rules, Ogden rate, a competitor exit. BOCPD tells you online whether this month looks like a break. PELT gives you retrospective break locations with bootstrap confidence intervals. The LossRatioMonitor combines both into a 'retrain / monitor' recommendation suitable for model governance.

Subpackages

insurance_dynamics.gas

  • GASModel — fits GAS(p,q) to a univariate series; Poisson, Gamma, NegBin, LogNormal, Beta, ZIP
  • GASPanel — fits the same spec across a set of rating cells
  • gas_forecast — h-step-ahead forecasts with simulation intervals
  • bootstrap_ci — parametric bootstrap confidence intervals on the filter path
  • compute_diagnostics — Dawid-Sebastiani score, PIT histograms, autocorrelation of score residuals

insurance_dynamics.changepoint

  • FrequencyChangeDetector — online BOCPD for claim frequency with exposure weighting
  • SeverityChangeDetector — online BOCPD on log-severity using Normal-Gamma conjugate
  • LossRatioMonitor — joint monitoring returning retrain/monitor recommendation
  • RetrospectiveBreakFinder — offline PELT with bootstrap confidence intervals
  • UKEventPrior — UK insurance event calendar (Ogden, whiplash reform, FCA pricing review)
  • ConsumerDutyReport — FCA PRIN 2A.9 evidence pack as HTML and JSON

Installation

pip install insurance-dynamics

Quick start

The GAS example uses the built-in dataset loader — no data setup required:

from insurance_dynamics.gas import GASModel
from insurance_dynamics.gas.datasets import load_motor_frequency

data = load_motor_frequency(T=48)
model = GASModel("poisson")
result = model.fit(data.y, exposure=data.exposure)
print(result.summary())
result.filter_path.plot()

The changepoint monitor requires time series arrays. Here is a minimal self-contained example with 60 months of synthetic UK motor data including a regime shift at month 36:

import numpy as np
from insurance_dynamics.changepoint import LossRatioMonitor

rng = np.random.default_rng(42)
T = 60

# Monthly exposure (earned car years)
exposures = rng.uniform(800, 1200, T)

# Claim frequency: base 0.08/year, step up 37.5% at month 36
true_rate = np.where(np.arange(T) < 36, 0.08, 0.11)
counts = rng.poisson(true_rate * exposures)

# Mean severity (log-normal, slight upward drift)
mean_sev = 1500 * np.exp(0.003 * np.arange(T)) * rng.lognormal(0, 0.15, T)

# Period labels
periods = [f"2021-{(i % 12) + 1:02d}" for i in range(T)]

monitor = LossRatioMonitor(lines=["motor"], uk_events=True)
result = monitor.monitor(
    claim_counts=counts,
    exposures=exposures,
    mean_severities=mean_sev,
    periods=periods,
)
print(result.recommendation)  # 'retrain' or 'monitor'

Design decisions

GAS and changepoint detection are complementary tools. GAS smooths the signal continuously; BOCPD detects discrete jumps. Using both gives you a full picture: is the drift smooth (GAS will catch it) or structural (BOCPD will flag it)? The LossRatioMonitor is deliberately opinionated — it returns a binary recommendation, not a probability, because pricing teams need an action, not a number.

Performance

Benchmarked against a static Poisson GLM (intercept-only and linear trend variants) on 60 months of synthetic UK motor frequency data with a known regime shift at month 36 (frequency drops 37.5%). Full notebook: notebooks/benchmark.py.

Metric Static GLM GAS Poisson
One-step-ahead MAE (overall) higher lower
One-step-ahead MAE (post-break) higher lower
Lambda RMSE vs true schedule (post-break) higher lower
Log-likelihood lower higher
PIT KS test (calibration) fails post-break passes
Break detected within ±3 months No BOCPD + PELT: Yes

The static GLM blends the two regimes into a long-run average — it is wrong in both directions and cannot flag that anything changed. The GAS filter adapts observation by observation; BOCPD and PELT locate the break retrospectively with a bootstrap confidence interval.

When to use: You have monthly or quarterly aggregate claim data and want to track how underlying rates are evolving, or you need to detect whether a regulatory event or market shift has changed your book.

When NOT to use: You have a stable, stationary book and want to explain cross-sectional risk factors. That is a GLM job. GAS adds value at the time-series layer, not the rating-factor layer.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

insurance_dynamics-0.1.0.tar.gz (191.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

insurance_dynamics-0.1.0-py3-none-any.whl (67.6 kB view details)

Uploaded Python 3

File details

Details for the file insurance_dynamics-0.1.0.tar.gz.

File metadata

  • Download URL: insurance_dynamics-0.1.0.tar.gz
  • Upload date:
  • Size: 191.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for insurance_dynamics-0.1.0.tar.gz
Algorithm Hash digest
SHA256 9b73701b4cc63e99fdadcd4fbda068960df232baa0fc6e5dd5acca68fd2cb2a3
MD5 58f4a7f69d02d1cd8106e0fe742156d4
BLAKE2b-256 74bf84b03a144c451900a076da4b435d47b0211c2c34ff3ec02011b67549d9c9

See more details on using hashes here.

File details

Details for the file insurance_dynamics-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: insurance_dynamics-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 67.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.8 {"installer":{"name":"uv","version":"0.10.8","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for insurance_dynamics-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 eb437cf7bc1a9e6f820e965770f947e90f39e0f27d6a50a722c0f54bce2803c9
MD5 b3dd8a9558a90f6749853a5211e6311b
BLAKE2b-256 4b51fbd0e89e0c53bffebefe2681b10e61eed35ac3c0c920ea3a1c5965d3d557

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page