Metadata-Version: 2.4
Name: relativities
Version: 0.2.0
Summary: Exact insurance rating and portfolio analysis in Python
Keywords: insurance,actuarial,rating,pricing,ratemaking,rsl
Author: EffectiveAI
Author-email: EffectiveAI <support@effectiveailabs.com>
License-Expression: LicenseRef-Proprietary
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Office/Business :: Financial
Classifier: Topic :: Scientific/Engineering :: Mathematics
Classifier: Typing :: Typed
Requires-Dist: httpx>=0.28,<1
Requires-Dist: pydantic>=2.12,<3
Requires-Dist: rslc==0.3.0.post1
Requires-Dist: numpy>=2,<3 ; extra == 'models'
Requires-Dist: scipy>=1.15,<2 ; extra == 'models'
Requires-Dist: statsmodels>=0.14.6,<0.15 ; extra == 'models'
Requires-Dist: pandas>=2.2,<4 ; extra == 'pandas'
Requires-Dist: matplotlib>=3.10,<4 ; extra == 'plot'
Requires-Python: >=3.12
Project-URL: Documentation, https://docs.effectiveai.app
Project-URL: Homepage, https://effectiveai.app
Provides-Extra: models
Provides-Extra: pandas
Provides-Extra: plot
Description-Content-Type: text/markdown

# Relativities

Exact insurance rating and portfolio analysis in Python, on your own machine.

Relativities runs rating programs written in RSL, EffectiveAI's rating
language, on a native Rust engine. Build a small rater in Python or load one
from EffectiveAI, rate one submission or a whole book, and analyze the results
with weights, exact decimals and every refusal kept in view. What you rate
stays on your computer.

## Install

```sh
pip install relativities
pip install "relativities[plot]"   # extras: pandas, models, plot
```

Requires Python 3.12 or newer. Prebuilt wheels cover macOS (Intel and Apple
Silicon), Linux (x86_64 and aarch64, glibc 2.28+) and Windows (x86_64), so no
compiler is needed.

## Quickstart: build a rater and rate a book

```python
from decimal import Decimal
from relativities import RSL, Dataset

program = RSL.Program("Home")
territory = program.enum("Territory", {"coastal": "coastal", "inland": "inland"})

coverage_a = program.input("coverage_a", precision="int", bounds=(100_000, 1_500_000))
home_territory = program.input("territory", dtype=territory)
claims_free = program.input("claims_free", dtype="Bool")

base_rate = program.table(
    "base_rate_per_1000", key=territory, rows=[("coastal", "4.10"), ("inland", "2.60")]
)
base = program.calculate("base_premium", coverage_a * base_rate(home_territory) * Decimal("0.001"))
discount = claims_free.choose(Decimal("0.90"), 1)
program.output("premium", (base * discount).round(2, mode="half_up"))

rate = program.compile()

homes = Dataset.from_records([
    {"id": "H1", "Home": {"coverage_a": 400_000, "territory": "inland", "claims_free": True}},
    {"id": "H2", "Home": {"coverage_a": 900_000, "territory": "coastal", "claims_free": False}},
    {"id": "H3", "Home": {"coverage_a": 2_000_000, "territory": "coastal", "claims_free": True}},
])
results = rate(homes)

for row in results.rows:
    print(row.id, row.status, row.outputs["premium"] if row.status == "succeeded" else row.error.code)
# H1 succeeded 936
# H2 succeeded 3690
# H3 invalid_inputs INPUT-RANGE
```

H3 is above the coverage limit, so it comes back as `invalid_inputs` with a
named reason instead of disappearing or becoming a zero premium.

## Analyze a book

Wrap your own records with `Dataset.from_records(...)` or
`Dataset.from_pandas(df, key="policy_id")`, then add representation weights
with `Population`. This example uses the bundled synthetic auto rater and
households:

```python
from operator import itemgetter
from relativities import Population, load_dataset, pipeline

households = load_dataset("example-auto-households")
population = Population(households, unit="household", weights="weight")

with pipeline("rating", rater="builtin://example-auto", preprocess=itemgetter("submission")) as rate:
    ratings = rate(households)

summary = ratings.summarize(output="/Policy/annual_premium", weights=population.weights)
print(summary.mean, summary.quantile(0.95))   # 640.625 1375
print(summary.rated_count, summary.unrated_count)   # 5 1
print([row.id for row in ratings.refusals])   # ['mileage-refusal']
```

Every input keeps exactly one result row and its ID. Rows are `succeeded`,
`invalid_inputs` (malformed or out-of-range), `refused` (a coded business rule)
or a mapping or runtime failure. Summaries report rated and unrated weight side
by side, and a missing output is never treated as zero.

## Rate EffectiveAI raters

With an EffectiveAI API key, catalog raters download once, are verified and
cached, then rate locally:

```sh
export EFFECTIVE_API_KEY=sk-eai-...
```

```python
from relativities import Hub, Rater

with Hub() as hub:
    print([row["raterId"] for row in hub.list_raters()])

with Rater.load("<rater-id>") as rater:
    print(rater.input_schema)          # this rater's submission shape
    result = rater(submission)
```

| Setting                                    | Environment variable     | Argument                |
| ------------------------------------------ | ------------------------ | ----------------------- |
| API key                                    | `EFFECTIVE_API_KEY`      | `api_key=`              |
| Server (default `https://effectiveai.app`) | `EFFECTIVE_API_BASE_URL` | `Hub(base_url=...)`     |
| Cache only, no network                     | `EFFECTIVE_OFFLINE=1`    | `local_files_only=True` |

Pass `revision=` with a bundle ID to pin an exact version; a pinned rater then
loads from the cache offline. Submissions and results are never uploaded.

Some catalog raters still use an older RSL version and can't be rated locally
yet; `Rater.load` says so when that's the case.

## Fit a simple loss model

Requires the `models` extra:

```python
from relativities import Dataset, LossModel, evaluate, load_dataset, pipeline

experience = load_dataset("example-claims")
training = Dataset([row for _, row in experience.iter_rows() if row["split"] == "train"])
holdout = Dataset([row for _, row in experience.iter_rows() if row["split"] == "test"])

frequency = LossModel(
    family="poisson",
    features=("driver_age",),
    target="claim_count",
    exposure="exposure",
    unit="claims over supplied exposure",
).fit(training)

counts = pipeline("loss", model=frequency)(holdout).distributions["claim_count"]
print(counts.mean())
print(counts.survival(2))  # P(N > 2) for each record
print(evaluate(frequency, data=holdout).summary())
```

## Why the numbers hold up

- **Exact arithmetic.** Premiums are exact decimals. A value with no finite
  decimal form, such as one third, stays an exact fraction
  (`{"$rational": "1/3"}`) instead of being silently rounded.
- **Pinned and verified.** Every rater is identified by an immutable bundle ID
  and its files are verified before anything runs.
- **One engine.** Raters you build in Python and catalog raters both run on the
  same Rust engine that checks the RSL before rating.

## Scope

- The Python builder covers one flat record with numeric, Boolean and enum
  inputs, `+`, `-`, `*`, equality, conditionals (`choose`), single-key tables
  and rounding. Load a full RSL rater for anything more.
- Loss models are Poisson frequency (with optional exposure) and Gamma
  severity GLMs with a log link and numeric predictors. They don't model
  parameter uncertainty or portfolio dependence.
- The bundled datasets are small synthetic examples, not market data.

## License and support

Relativities is proprietary software from EffectiveAI, licensed under the terms
in the `LICENSE` file included with the package. Catalog raters and data are
covered by separate agreements.

- Website: <https://effectiveai.app>
- Documentation: <https://docs.effectiveai.app>
- Support: [support@effectiveailabs.com](mailto:support@effectiveailabs.com)
