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

# relativities.ratemaking

> Develop losses, fit trends, on-level premium and indicate an overall rate change.

*Generated from the package's Python API.*

Experience and the overall rate indication, in exact arithmetic.

`Triangle` develops losses to ultimate: `factors` averages its link ratios into a table
to edit in pandas, and `develop` projects each origin with it. `Trend` fits or holds
an annual trend and gives the factor between two points in time. `RateHistory` gives
parallelogram on-level factors for calendar-year premium. `Indication` turns a projected
loss ratio and expense provisions into the indicated overall change; `with_credibility`
blends it with a complement, weighted by `credibility`, and `explain` shows each step.

Every amount is a `Decimal` and every factor is exact to 38 significant digits; floats are
refused where they would carry binary rounding into a rate. Rating relativities by class
come from `relativities.models.FactorModel`.

**Develop losses**

| Name | What it is |
| - | - |
| [`Triangle`](#triangle) | Cumulative losses (or counts) by origin period and development age. |

**Trend and on-level**

| Name | What it is |
| - | - |
| [`Trend`](#trend) | An annual exponential trend, such as 4% a year in claim severity. |
| [`RateHistory`](#ratehistory) | Overall rate changes by effective date, such as `{date(2023, 7, 1): Decimal("0.05")}`. |

**Indicate a change**

| Name | What it is |
| - | - |
| [`Indication`](#indication) | The overall rate change the loss ratio method indicates. |
| [`credibility`](#credibility) | Limited fluctuation credibility: `min(1, sqrt(claims / standard))`. |
| [`full_credibility`](#full_credibility) | The expected claims for full credibility under limited fluctuation: `(z / tolerance) ** 2`, where `z` is the standard normal quantile of `(1 + probability) / 2`. |

## Triangle

```python theme={null}
class Triangle(
    claims: pd.DataFrame,
    *,
    origin: str,
    age: str,
    value: str,
    cumulative: bool = True,
)
```

Cumulative losses (or counts) by origin period and development age.

Build one from a long table with one row per origin and age; `origin` is
the accident, policy or report period and `age` its development age as a
positive integer, such as months. Values are exact `Decimal`s. Each
origin's ages are the first ages of the triangle in sequence, so each link
ratio compares the same origin one age apart.

`factors` averages the link ratios into one selected factor per age and
`develop` projects each origin to ultimate with a factors table. Edit the
`factor` column of `factors()` in pandas to make your own selections,
then pass it to `develop`.

### Triangle.origins

```python theme={null}
@property
def origins(self) -> list[Hashable]
```

The origin periods, in order.

### Triangle.ages

```python theme={null}
@property
def ages(self) -> list[int]
```

The development ages, in order.

### Triangle.to\_pandas

```python theme={null}
def to_pandas(self) -> pd.DataFrame
```

The cumulative triangle: one row per origin and one column per age. `NaN` marks
each age beyond an origin's latest.

### Triangle.link\_ratios

```python theme={null}
def link_ratios(self) -> pd.DataFrame
```

Each origin's age-to-age ratios: one column per age, the ratio from that age to the
next. `NaN` marks a ratio beyond an origin's latest age, or from a zero value.

### Triangle.factors

```python theme={null}
def factors(
    self,
    *,
    average: Average = 'volume',
    periods: int | None = None,
    tail: Decimal | int = 1,
) -> pd.DataFrame
```

One selected factor per age, and the cumulative factor to ultimate.

`average` is one of the averages of Werner and Modlin's development exhibits:

* `volume`: the sum of later values over the sum of earlier values.
* `simple`: the straight average of the link ratios.
* `medial`: the straight average after excluding the highest and the lowest ratio,
  when at least three ratios remain to choose from.
* `geometric`: the nth root of the product of the n link ratios.

`periods` keeps only the latest origins that reach each pair of ages, such as the
latest three. `tail` is the factor from the
last age to ultimate. The last row is the tail.

Columns: `age`, `factor` (from this age to the next, or to ultimate on the last
row), `cdf` (from this age to ultimate) and `origins` (how many origins the
average used). Edit `factor` to select your own; `develop` reads only `age` and
`factor` and recalculates `cdf`.

### Triangle.develop

```python theme={null}
def develop(self, factors: pd.DataFrame | None = None) -> pd.DataFrame
```

Each origin projected to ultimate with a factors table (`factors()` by default).

Each `factor` develops from its `age` to the next age in the table, and the last
row develops to ultimate. An age's cumulative factor is the product of the factors at
that age and every later age in the table, so a table selected on a longer triangle
keeps its later factors and tail. The table needs one row for each age of this
triangle; ages are unique.

Columns: `origin`, `age` (its latest age), `latest` (its latest value), `cdf`,
`ultimate` (`latest` times `cdf`) and `unreported` (`ultimate` less
`latest`: IBNR for reported losses, unpaid for paid losses).

## Trend

```python theme={null}
class Trend(rate: Decimal | int)
```

An annual exponential trend, such as 4% a year in claim severity.

`Trend(Decimal("0.04"))` is a selected trend; `Trend.fit` fits one to a series by
least squares on the logarithm of its values. `factor(start, end)` is
`(1 + rate) ** years` between two points in time, each a date or a number of years,
such as the average accident date of an experience period and of the future policy
period. A date counts its months as twelfths of a year, so 1 January 2011 to 1 July 2013
is 2.5 years.

### Trend.fit

```python theme={null}
@classmethod
def fit(cls, series: pd.DataFrame, *, period: str, value: str) -> Trend
```

Fit `value = a * (1 + rate) ** period` by least squares on `log(value)`.

`period` holds dates or numbers of years, such as each quarter's midpoint, and
`value` positive amounts, such as average severity or frequency. `observations`
on the result holds each point with its fitted value.

### Trend.rate

```python theme={null}
@property
def rate(self) -> Decimal
```

The annual rate: 0.04 is 4% a year.

### Trend.observations

```python theme={null}
@property
def observations(self) -> pd.DataFrame | None
```

The fitted series and each point's fitted value, or `None` for a selected trend.

### Trend.factor

```python theme={null}
def factor(self, start: object, end: object) -> Decimal
```

`(1 + rate) ** years` from `start` to `end`, each a date or a number of years.

## RateHistory

```python theme={null}
class RateHistory(changes: Mapping[object, Decimal | int])
```

Overall rate changes by effective date, such as `{date(2023, 7, 1): Decimal("0.05")}`.

The rate level is 1 before the first change and is multiplied by `1 + change` at each
effective date. `on_level` finds the factors that bring each calendar year's premium to
the current level by the parallelogram method of Werner and Modlin's Basic Ratemaking,
chapter 5: policies are written evenly through the year, each change applies to policies
written on or after its date, and premium is earned evenly over the term. A date counts its
months as twelfths of a year, as the textbook does: 1 July is 0.5. When you have the policies, rating them again at current rates with
`Program.rate` is the exact alternative.

### RateHistory.levels

```python theme={null}
def levels(self) -> pd.DataFrame
```

Each change with the cumulative rate level from its effective date.

### RateHistory.on\_level

```python theme={null}
def on_level(
    self,
    years: Iterable[int],
    *,
    basis: Basis = 'earned',
    term_months: int = 12,
) -> pd.DataFrame
```

On-level factors for calendar years.

`basis` is `earned` (calendar-year earned premium) or `written` (calendar-year
written premium). `term_months` is the policy term. Columns: `year`,
`average_level` (the year's average rate level), `current_level` and `factor`
(`current_level` over `average_level`, which multiplies the year's premium).

## Indication

```python theme={null}
class Indication(
    loss_ratio: Decimal | int,
    *,
    fixed_expense: Decimal | int = 0,
    variable_expense: Decimal | int = 0,
    profit: Decimal | int = 0,
)
```

The overall rate change the loss ratio method indicates.

`loss_ratio` is the projected ultimate loss and LAE ratio: developed, trended losses
over premium at current rate level. `fixed_expense` is the fixed expense ratio at
current rates, `variable_expense` the variable expense ratio and `profit` the profit
and contingencies provision, each a share of premium. The indicated change is
`(loss_ratio + fixed_expense) / (1 - variable_expense - profit) - 1`.

`with_credibility` blends it with a complement, and `explain` shows each step.

### Indication.permissible\_loss\_ratio

```python theme={null}
@property
def permissible_loss_ratio(self) -> Decimal
```

The share of premium left for losses, LAE and fixed expenses:
`1 - variable_expense - profit`.

### Indication.indicated

```python theme={null}
@property
def indicated(self) -> Decimal
```

The indicated change from this experience alone, before credibility.

### Indication.change

```python theme={null}
@property
def change(self) -> Decimal
```

The indicated change after credibility: 0.05 is a 5% increase.

### Indication.with\_credibility

```python theme={null}
def with_credibility(
    self,
    credibility: Decimal | int,
    complement: Decimal | int,
) -> Indication
```

A copy whose change is `credibility * indicated + (1 - credibility) * complement`.

`credibility` is between 0 and 1, such as `credibility(claims)`. `complement` is
the change that takes the remaining weight, such as the trended change since the last
review or a larger book's indication.

### Indication.explain

```python theme={null}
def explain(self) -> pd.DataFrame
```

Each step of the indication: its name, value and formula.

## credibility

```python theme={null}
def credibility(claims: Decimal | int, *, standard: Decimal | int = 1082) -> Decimal
```

Limited fluctuation credibility: `min(1, sqrt(claims / standard))`.

`standard` is the claim count for full credibility, 1,082 by default; `full_credibility`
computes it for other probability and tolerance choices.

## full\_credibility

```python theme={null}
def full_credibility(
    *,
    probability: Decimal | int = Decimal('0.90'),
    tolerance: Decimal | int = Decimal('0.05'),
) -> Decimal
```

The expected claims for full credibility under limited fluctuation: `(z / tolerance) ** 2`,
where `z` is the standard normal quantile of `(1 + probability) / 2`. The defaults give
about 1,082 claims. The quantile is solved in `Decimal` to the module's 38 digits.


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