All checks were successful
CI / lint (push) Successful in 44s
CI / notebooks-smoke (push) Successful in 1m33s
Deploy / changes (push) Successful in 14s
CI / test (push) Successful in 3m9s
Infra CI / zotero (push) Successful in 14s
Infra CI / notebooks (push) Successful in 55s
Infra CI / api (push) Successful in 1m9s
Infra CI / docs (push) Successful in 1m23s
Infra CI / llm (push) Successful in 1m1s
Infra CI / mc (push) Successful in 16s
Infra CI / bcda (push) Successful in 1m43s
Deploy / notebooks (push) Has been skipped
Deploy / zotero (push) Has been skipped
Deploy / docs (push) Has been skipped
Package Supply Chain / pkg-supply-chain (push) Successful in 1m30s
Deploy / api (push) Successful in 1m17s
Deploy / llm (push) Successful in 1m18s
Deploy / mc (push) Has been skipped
Deploy / bcda (push) Successful in 1m42s
Deploy / report (push) Successful in 1m36s
Ports the public CMS "LEAD Model PY 2027 Alignment and Financial Methodology Paper" v1.2 (bib E4PEU3HV) as generic engines: parameters come from the paper, everything CMS computes per ACO or per year comes from the caller. No participant data, no organization-specific rules. - lead.params: Table 5.2.1 discounts, 3% Quality Withhold, 2% retention incentive, Box 5.3.3 Stop-Loss bands, Tables 5.4.1/5.4.3 corridors, 2% sequestration, base-year weights, Table 3.5.1 ACPT guardrails, risk-score growth caps, REA/PSA shares and caps, voluntary guardrail and 500 minimum, Table 2.6.1 base-year alignment minimums. - lead.settlement: Final Financial Settlement, Table 5.6.1 line by line (settle(SettlementInputs) -> SettlementLines). - lead.stop_loss: Box 5.3.3 charge, banded payout, neutrality factor, application to PY expenditure. - lead.monies_owed: Table 5.5.1 Total Monies Owed, APO reconciliation. - lead.timing: run-out end, settlement-basis rule, settlement-stage inputs. - lead.benchmark: §3 formulas — risk standardization, two-/three-way trend, base-year weights and sufficiency, voluntary guardrail, regional FFS, spending designation, REA, PSA, risk-score cap, hybrid alignment dollars, category blend. - lead.express: the same as narwhals Exprs; express.settlement.settle settles a frame of scenarios on any backend. Rounding: every dollar line rounds to the cent (half away from zero) as it is produced; PBPMs, rates and factors never do. It is the only rule under which Tables 3.3.3, 3.5.3.2, 3.5.4, 5.5.1 and 5.6.1 all reproduce (3.5.4 line 24 sums cent-rounded cells; 3.3.3 carries unrounded PBPMs). The Expr cents snaps its ÷100 with round(2): polars 2.0 divides a column by a literal as a reciprocal multiply (k × 0.01), one ulp off k/100 for ~15% of values. Missing inputs stay missing (None/null or a refused input model); the only zeros are the paper's own (Professional "N/A" discount, no Stop-Loss election, sequestration on losses, ineligible REA/PSA). Paper inconsistencies recorded in tests: Table 5.5.1 line 2 is a cent below Table 5.6.1 line 28 (.43 vs .44); Box 3.3.2 prints 1.104 then says 1.103; §3.5.1 prose widens the ACPT lower bound a year later than Table 3.5.1 (the table is followed). Tests: 283 in tests/lead — every published line to the cent, Expr builders on polars and DuckDB, bit-for-bit scalar/frame parity over 160 scenarios; 100% of src/lead.
88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
"""lead.express.stop_loss: beneficiary-grain Stop-Loss on polars and DuckDB,
|
|
matching Box 5.3.3 and the scalar engine."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import random
|
|
|
|
import polars as pl
|
|
|
|
from lead.express.stop_loss import (
|
|
band_status,
|
|
distance_to_attachment_point,
|
|
predicted_expenditure,
|
|
residual_expenditure,
|
|
stop_loss_payout,
|
|
)
|
|
from lead.stop_loss import band_status as scalar_band_status
|
|
from lead.stop_loss import stop_loss_payout as scalar_payout
|
|
from tests.lead.paper import STOP_LOSS_PAYOUT
|
|
|
|
SCHEMA = {"actual": pl.Float64, "predicted": pl.Float64, "ap": pl.Float64}
|
|
|
|
|
|
def test_box_5_3_3_illustration(backend):
|
|
data = [
|
|
{
|
|
"actual": STOP_LOSS_PAYOUT["actual"],
|
|
"predicted": STOP_LOSS_PAYOUT["predicted"],
|
|
"ap": STOP_LOSS_PAYOUT["attachment_point"],
|
|
}
|
|
]
|
|
residual = residual_expenditure("actual", "predicted")
|
|
(result,) = backend.select(
|
|
data,
|
|
SCHEMA,
|
|
residual=residual,
|
|
payout=stop_loss_payout(residual, "ap"),
|
|
status=band_status(residual, "ap"),
|
|
distance=distance_to_attachment_point("ap", residual),
|
|
)
|
|
assert result == {
|
|
"residual": 400_000.0,
|
|
"payout": 220_000.0,
|
|
"status": "band_2",
|
|
"distance": -250_000.0,
|
|
}
|
|
|
|
|
|
def test_predicted_expenditure(backend):
|
|
data = {"regional": [1_000.0], "risk": [1.2], "months": [12.0]}
|
|
(result,) = backend.select(
|
|
data, predicted=predicted_expenditure("regional", "risk", "months")
|
|
)
|
|
assert result["predicted"] == 14_400.0
|
|
|
|
|
|
def test_payout_and_band_match_the_scalar(backend):
|
|
rng = random.Random(3)
|
|
data = [
|
|
{
|
|
"actual": rng.uniform(0, 900_000),
|
|
"predicted": rng.uniform(0, 200_000),
|
|
"ap": rng.choice([150_000.0, 98_765.43]),
|
|
}
|
|
for _ in range(200)
|
|
]
|
|
data += [
|
|
{"actual": None, "predicted": 1.0, "ap": 150_000.0},
|
|
{"actual": 1.0, "predicted": 0.0, "ap": None},
|
|
]
|
|
residual = residual_expenditure("actual", "predicted")
|
|
rows = backend.select(
|
|
data,
|
|
SCHEMA,
|
|
residual=residual,
|
|
payout=stop_loss_payout(residual, "ap"),
|
|
status=band_status(residual, "ap"),
|
|
)
|
|
for inputs, result in zip(data, rows, strict=True):
|
|
if inputs["actual"] is None or inputs["ap"] is None:
|
|
assert (result["payout"], result["status"]) == (None, None)
|
|
continue
|
|
assert (
|
|
result["payout"]
|
|
== scalar_payout(result["residual"], inputs["ap"]).total_payout
|
|
)
|
|
assert result["status"] == scalar_band_status(result["residual"], inputs["ap"])
|