> ## Documentation Index
> Fetch the complete documentation index at: https://docs.effectiveai.app/llms.txt
> Use this file to discover all available pages before exploring further.

# Indicate an overall rate change

> Develop losses to ultimate, trend them, bring premium to current rates and calculate the indicated change with the loss ratio method.

This guide takes three accident years of synthetic collision experience to an indicated overall rate change. Every amount stays an exact `Decimal` from the paid losses to the indication.

```python theme={null}
from datetime import date
from decimal import Decimal as D
import pandas as pd
from relativities.ratemaking import Indication, RateHistory, Trend, Triangle, credibility
```

## Develop losses to ultimate

Build a triangle from one row per accident year and age. Ages are months of development, and values are cumulative paid losses and loss adjustment expenses (LAE):

```python theme={null}
paid = pd.DataFrame({
    "accident_year": [2021, 2021, 2021, 2022, 2022, 2023],
    "age": [12, 24, 36, 12, 24, 12],
    "paid": [D("4100000"), D("5900000"), D("6450000"), D("4400000"), D("6400000"), D("4700000")],
})
triangle = Triangle(paid, origin="accident_year", age="age", value="paid")
factors = triangle.factors(average="volume", tail=D("1.02"))
factors[["age", "factor", "origins"]].assign(factor=factors.factor.map(lambda f: round(f, 4)))
#    age  factor  origins
# 0   12  1.4471        2
# 1   24  1.0932        1
# 2   36  1.0200        0
```

`factors` averages each column of link ratios with one of the averages from Werner and Modlin's development exhibits. `volume` divides the sum of later values by the sum of earlier values, and `simple` averages the ratios. `medial` averages them after excluding the highest and lowest, and `geometric` takes the nth root of their product. `periods=3` keeps the latest three origins for each pair of ages. The last row is the tail factor from 36 months to ultimate.

The factors table is your selection. To select a factor yourself, edit its `factor` cell in pandas. `develop` reads `age` and `factor`, and recalculates each cumulative factor:

```python theme={null}
developed = triangle.develop(factors)
developed[["origin", "latest", "ultimate"]].assign(ultimate=developed.ultimate.map(lambda u: round(u)))
#    origin    latest  ultimate
# 0    2021   6450000   6579000
# 1    2022   6400000   7136542
# 2    2023   4700000   7583888
```

`unreported` holds ultimate less latest: unpaid losses for a paid triangle, IBNR for a reported one.

## Bring premium to current rates

Record each overall rate change by its effective date. `on_level` gives the parallelogram factor for each calendar year of earned premium, for annual policies written evenly through the year:

```python theme={null}
history = RateHistory({date(2021, 7, 1): D("0.04"), date(2023, 1, 1): D("0.06")})
on_level = history.on_level([2021, 2022, 2023])
on_level[["year", "factor"]].assign(factor=on_level.factor.map(lambda f: round(f, 4)))
#    year  factor
# 0    2021  1.0969
# 1    2022  1.0651
# 2    2023  1.0291
```

A date counts months as twelfths of a year, as in the textbook: 1 July is 0.5 and 1 April is 0.25, and a day within a month adds its share of that month. `basis="written"` gives policy-year factors: the average rate level of the policies written in each year. `term_months` sets another policy term. To rate each historical policy again at current rates, use [`Program.rate`](https://docs.effectiveai.app/relativities/how-to/rate-a-book-with-a-program) with the current program; that gives exact on-level premium policy by policy.

## Fit a trend

Fit an annual exponential trend to quarterly average severity. Each period is a number of years, or a date:

```python theme={null}
severity = pd.DataFrame({
    "quarter": [D("2021.125") + D(q) / 4 for q in range(12)],
    "severity": [D(v) for v in ("4510", "4560", "4590", "4650", "4700", "4720",
                                "4790", "4830", "4880", "4920", "4990", "5040")],
})
trend = Trend.fit(severity, period="quarter", value="severity")
round(trend.rate, 4)
# Decimal('0.0408')
```

`trend.observations` holds each quarter with its fitted value. For a selected trend, write `Trend(D("0.04"))`. `trend.factor(start, end)` projects from one point in time to another.

## Calculate the indication

Trend each year's ultimate losses from its average accident date to the average accident date of the future policy period. Divide by on-level earned premium:

```python theme={null}
earned = pd.DataFrame({"year": [2021, 2022, 2023], "premium": [D("9800000"), D("10400000"), D("11100000")]})
experience = developed.merge(on_level, left_on="origin", right_on="year").merge(
    earned, on="year")
experience["trended"] = [u * trend.factor(date(y, 7, 1), date(2026, 1, 1))
                         for u, y in zip(experience.ultimate, experience.origin)]
experience["on_level_premium"] = experience.premium * experience.factor
loss_ratio = experience.trended.sum() / experience.on_level_premium.sum()
round(loss_ratio, 4)
# Decimal('0.7357')
```

Give the expense and profit provisions as shares of premium:

```python theme={null}
indication = Indication(loss_ratio, fixed_expense=D("0.06"), variable_expense=D("0.18"), profit=D("0.05"))
round(indication.change, 4)
# Decimal('0.0334')
```

The experience indicates a 3.34% increase: `(loss_ratio + fixed_expense) / (1 - variable_expense - profit) - 1`.

## Weight the indication by credibility

Blend the indication with a complement, such as the trended change since the last review. `credibility` gives limited fluctuation credibility from a claim count, with 1,082 claims for full credibility:

```python theme={null}
blended = indication.with_credibility(credibility(860), complement=D("0.03"))
blended.explain()[["step", "value"]].assign(value=lambda t: t.value.map(lambda v: round(v, 4)))
#                      step   value
# 0              loss_ratio  0.7357
# 1           fixed_expense  0.0600
# 2        variable_expense  0.1800
# 3                  profit  0.0500
# 4  permissible_loss_ratio  0.7700
# 5               indicated  0.0334
# 6             credibility  0.8915
# 7              complement  0.0300
# 8                  change  0.0331
```

860 claims give 89% credibility, and the credibility-weighted change is 3.31%. `full_credibility(probability=..., tolerance=...)` gives the standard for other choices. To spread the change across rating factors, [fit relativities from experience](https://docs.effectiveai.app/relativities/how-to/fit-relativities-from-experience.md).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.