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, ZIPGASPanel— fits the same spec across a set of rating cellsgas_forecast— h-step-ahead forecasts with simulation intervalsbootstrap_ci— parametric bootstrap confidence intervals on the filter pathcompute_diagnostics— Dawid-Sebastiani score, PIT histograms, autocorrelation of score residuals
insurance_dynamics.changepoint
FrequencyChangeDetector— online BOCPD for claim frequency with exposure weightingSeverityChangeDetector— online BOCPD on log-severity using Normal-Gamma conjugateLossRatioMonitor— joint monitoring returning retrain/monitor recommendationRetrospectiveBreakFinder— offline PELT with bootstrap confidence intervalsUKEventPrior— 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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9b73701b4cc63e99fdadcd4fbda068960df232baa0fc6e5dd5acca68fd2cb2a3
|
|
| MD5 |
58f4a7f69d02d1cd8106e0fe742156d4
|
|
| BLAKE2b-256 |
74bf84b03a144c451900a076da4b435d47b0211c2c34ff3ec02011b67549d9c9
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
eb437cf7bc1a9e6f820e965770f947e90f39e0f27d6a50a722c0f54bce2803c9
|
|
| MD5 |
b3dd8a9558a90f6749853a5211e6311b
|
|
| BLAKE2b-256 |
4b51fbd0e89e0c53bffebefe2681b10e61eed35ac3c0c920ea3a1c5965d3d557
|