Skip to main content

Focused actuarial claim, premium, membership, and expense projections.

Project description

projectionmodels

CI PyPI

Focused actuarial projections of claims, premium, and expenses on supplied exposure.

The package is intentionally organized around concrete workflows. Most users should not need to construct a calculation graph or define a custom state engine.

Installation

pip install projectionmodels

projectionmodels currently supports actuarialpy>=0.41,<0.45 and Python 3.10–3.13.

Public API

The package root contains the workflow objects most actuaries need:

ClaimExperience        Prepare a base claim rate from experience
ClaimProjection        Project claim rates and claims by claim type
PremiumProjection      Roll premium forward, including renewal rate actions
RenewalRateActions     Supply effective-dated rate actions
ExpenseProjection      Project per-exposure, fixed, premium-based, and claim-based expenses
ProjectionHorizon      Define monthly, quarterly, or annual projection periods
ProjectionDates        Define entry, exit, renewal, and experience date columns
DateCohort              Split records into existing/new or other date cohorts
Adjustment / Scenario  Run sensitivities and alternative assumptions
ProjectionResults      Summarize without averaging ratios or duplicating exposure

Lower-level modeling objects are available from projectionmodels.advanced, but they are not part of the primary workflow.

Premium at renewal

import pandas as pd
import projectionmodels as pm

premium_data = pd.DataFrame(
    {
        "group_id": ["A", "B"],
        "renewal_date": pd.to_datetime(["2027-03-01", "2027-07-01"]),
        "current_premium_rate": [100.0, 100.0],
        "rate_action": [0.10, 0.20],
    }
)

periods = pd.period_range("2027-01", periods=12, freq="M").astype(str)
exposure = pd.DataFrame(
    [
        {
            "group_id": group_id,
            "projection_period": period,
            "member_months": 1_000.0,
        }
        for group_id in ("A", "B")
        for period in periods
    ]
)

results = pm.PremiumProjection(
    premium_data=premium_data,
    projection_keys=["group_id"],
    exposure=exposure,
    exposure_col="member_months",
    horizon=pm.ProjectionHorizon("2027-01-01", periods=12),
    recurring_rate_action_col="rate_action",
).project()

Group A remains at $100 through February, increases to $110 in March, and carries that rate forward. Group B increases to $120 in July.

For different actions at different renewals, provide an effective-dated table:

actions = pm.RenewalRateActions(
    pd.DataFrame(
        {
            "group_id": ["A", "A", "B"],
            "effective_date": pd.to_datetime(
                ["2027-03-01", "2028-03-01", "2027-07-01"]
            ),
            "rate_action": [0.10, 0.06, 0.20],
        }
    ),
    projection_keys=["group_id"],
)

Claims by claim type

experience = pm.ClaimExperience(
    claims,
    projection_keys=["group_id", "product_id"],
    claim_type_col="claim_type",
    date_col="incurred_month",
    claims_col="reported_claims",
    exposure_col="member_months",
    valuation_date="2026-12-31",
)

projection = pm.ClaimProjection.from_experience(
    experience,
    exposure=exposure,
    exposure_col="member_months",
    horizon=pm.ProjectionHorizon("2027-01-01", periods=36),
    completion=completion,
    trend=trend,
    seasonality=seasonality,
    credibility=credibility,
    complement=manual_rates,
)

results = projection.project()

Trend, seasonality, completion, and credibility may be supplied directly as assumption tables.

Cost levels and pipeline order

The claim workflow evaluates, in order: complete → deseasonalize → trend the experience rate to the blend basis → credibility blend → trend from the basis to each projection period → reseasonalize → add rate_loads → multiply by exposure. Exposure is whatever unit the book uses — member-months, policy months, car-years — named with exposure_col.

The complement is used as stated. By default the blend basis is the prospective midpoint of the horizon (complement_basis="prospective"), the level at which manual and book rates are conventionally quoted — so a zero-credibility projection reproduces the complement rather than a trended copy of it. Set complement_basis="experience" if your complement is quoted at experience-period cost level, or pass an explicit as-of date. Because the month arithmetic is exactly additive, results at full credibility are identical under every basis.

rate_loads (for example a pooling charge) are added to the projected rate as stated: flat across periods, after seasonality, outside the blend.

Estimating assumptions with actuarialpy

Estimation is explicit and separate from projection execution:

from projectionmodels.integrations.actuarialpy import (
    estimate_completion,
    estimate_credibility,
    estimate_seasonality,
    estimate_trend,
)

completion = estimate_completion(
    "claim_completion",
    payment_history,
    by=["claim_type"],
    origin_col="incurred_month",
    valuation_col="paid_month",
    amount_col="paid_claims",
)

seasonality = estimate_seasonality(
    "claim_seasonality",
    completed_history,
    by=["claim_type"],
    date_col="incurred_month",
    value_col="completed_claims",
    exposure_col="member_months",
)

trend = estimate_trend(
    "claim_trend",
    deseasonalized_history,
    by=["claim_type"],
    date_col="incurred_month",
    value_col="deseasonalized_claims",
    exposure_col="member_months",
)

credibility = estimate_credibility(
    "claim_credibility",
    experience_history,
    method="limited_fluctuation",
    by=["group_id", "claim_type"],
    exposure_col="claim_count",
    full_credibility_standard=2_000,
)

The returned assumptions retain indicated values and diagnostics. An actuary can replace the indication while preserving the audit trail:

selected_trend = trend.select(selected_table, note="2027 pricing selection")

Expenses

ExpenseProjection supports:

  • per_exposure
  • fixed_monthly
  • percent_premium
  • percent_claims

Each expense type may have its own trend and projection component.

Date handling

ProjectionDates supports entry, exit, renewal, issue, and experience dates. Records can be inactive before entry or after exit, and exposure can be whole- period or daily-prorated.

DateCohort adds reportable classifications such as existing versus new business:

records = pm.DateCohort(
    "business_origin",
    "effective_date",
    split_date="2027-01-01",
    before_label="existing",
    on_or_after_label="new_business",
).apply(records)

Results

summary = results.summarize(
    by=["scenario", "product_id", "calendar_year"],
    measures=["member_months", "premium", "projected_claims", "claims_per_exposure"],
)

ProjectionResults retains measure grain. It counts exposure once when claim type is removed from a summary and recalculates per-exposure rates and loss ratios from their summed numerators and denominators.

Advanced models

Custom deterministic roll-forwards remain available, but are deliberately moved out of the primary namespace:

import projectionmodels.advanced as pma

model = pma.ProjectionModel(...)

Use this only when the claim, premium, and expense workflows are insufficient. The advanced API remains provisional while the concrete workflows stabilize.

Examples

Primary examples:

examples/health_claims.py
examples/pooled_claims.py
examples/calculated_assumptions.py
examples/renewal_rate_actions.py
examples/date_cohorts.py
examples/expenses.py
examples/underwriting_results.py

Custom-engine examples are under examples/advanced/.

Testing

The test suite imports the installed actuarialpy; it does not replace it with a session-wide fake. CI runs the tests, every example, package builds, and a clean wheel-install smoke test.

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

projectionmodels-0.6.3.tar.gz (71.5 kB view details)

Uploaded Source

Built Distribution

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

projectionmodels-0.6.3-py3-none-any.whl (46.7 kB view details)

Uploaded Python 3

File details

Details for the file projectionmodels-0.6.3.tar.gz.

File metadata

  • Download URL: projectionmodels-0.6.3.tar.gz
  • Upload date:
  • Size: 71.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for projectionmodels-0.6.3.tar.gz
Algorithm Hash digest
SHA256 eb6102550c71e41bfb4d46d8e9fa87a75bb3f1c9928c8608bd0b9ff426de360f
MD5 8d59e1e2dba03974f56100fd08f0575c
BLAKE2b-256 3153f6083cabc8aee0fc37194848842c4e57bd7bc4cf20dc65f64d60f4bc098c

See more details on using hashes here.

Provenance

The following attestation bundles were made for projectionmodels-0.6.3.tar.gz:

Publisher: release.yml on OpenActuarial/projectionmodels

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file projectionmodels-0.6.3-py3-none-any.whl.

File metadata

File hashes

Hashes for projectionmodels-0.6.3-py3-none-any.whl
Algorithm Hash digest
SHA256 2aba6389129cc48c47233581e32c3dcbbd01ecab11259fed4d2ed05b95bdf03d
MD5 b8e0341d9af2566e33207f56f5c63631
BLAKE2b-256 43a5833f06fd842c2bc94477d355b3e785dbadd5060db88814c5456078215075

See more details on using hashes here.

Provenance

The following attestation bundles were made for projectionmodels-0.6.3-py3-none-any.whl:

Publisher: release.yml on OpenActuarial/projectionmodels

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

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