Files
stack/src/lead/express/benchmark.py
kert cb9976e160
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
feat(lead): LEAD PY2027 financial methodology — settlement and benchmark engines
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.
2026-10-27 14:11:10 -04:00

351 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Performance Year Benchmark formulas (§3) as narwhals expressions.
Row-wise builders for a frame with one row per benchmark cell (category ×
alignment type, county, base year…), plus :func:`month_weighted_mean` for
``group_by(...).agg(...)``. Scalar twins: ``lead.benchmark``. A null or
non-positive denominator gives null, never a stand-in value.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.
"""
from __future__ import annotations
from collections.abc import Sequence
import narwhals as nw
from lead.benchmark import (
CAPPED,
CAPPED_AT_UPPER,
FLOORED,
RAISED_TO_LOWER,
WITHIN_CAP,
WITHIN_GUARDRAIL,
)
from lead.express import IntoExpr, cents, col, lit_or_col, ratio
from lead.params import (
ACPT_GUARDRAILS,
ACPT_WEIGHT,
FIRST_PERFORMANCE_YEAR,
GLOBAL,
HIGH_NEEDS,
LOWER_SPENDING,
REGIONAL_EFFICIENCY_SHARE,
RISK_SCORE_GROWTH_CAPS,
VOLUNTARY_GUARDRAIL_RATE,
)
RISK_CAP_REFERENCE_MISSING = "risk_growth_cap_reference_missing"
_NULL = nw.lit(None, dtype=nw.Float64())
# ── §3.3 ──────────────────────────────────────────────────────────────────
def risk_standardized_pbpm(
expenditure: IntoExpr, eligible_months: IntoExpr, average_risk_score: IntoExpr
) -> nw.Expr:
"""Expenditure ÷ eligible months ÷ average risk score (Table 3.3.3
lines 3 and 5); null without positive months and score.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.3.1 / Table 3.3.3.
"""
return ratio(ratio(expenditure, eligible_months), average_risk_score)
def growth_rate(later: IntoExpr, earlier: IntoExpr) -> nw.Expr:
"""later ÷ earlier FFS expenditures; null unless both are positive.
Source: LEAD PY2027 methodology (bib E4PEU3HV) Box 3.3.2 Steps 1–2.
"""
return nw.when(col(later) > 0).then(ratio(later, earlier))
def county_market_share(aligned_months: IntoExpr, eligible_months: IntoExpr) -> nw.Expr:
"""ACO-aligned ÷ alignment-eligible months in a county.
Source: LEAD PY2027 methodology (bib E4PEU3HV) Box 3.3.2 Step 3.1.
"""
return ratio(aligned_months, eligible_months)
def county_weight(aligned_months: IntoExpr, aco_aligned_months: IntoExpr) -> nw.Expr:
"""A county's share of the ACO's aligned months (trend weights and
regional FFS expenditures alike).
Source: LEAD PY2027 methodology (bib E4PEU3HV) Box 3.3.2 Step 3.2 / §3.4.1.
"""
return ratio(aligned_months, aco_aligned_months)
def two_way_blended_trend(
national_growth: IntoExpr, regional_growth: IntoExpr, national_weight: IntoExpr
) -> nw.Expr:
"""national × weight + regional × (1 − weight).
Source: LEAD PY2027 methodology (bib E4PEU3HV) Box 3.3.2 Step 4.
"""
weight = col(national_weight)
return col(national_growth) * weight + col(regional_growth) * (1.0 - weight)
def trended_weighted_pbpm(
risk_standardized: IntoExpr, trend_to_by3: IntoExpr, base_year_weight: IntoExpr
) -> nw.Expr:
"""One base year's contribution to the Historical Benchmark: PBPM ×
trend to BY 3 × weight (Table 3.3.3 line 9); sum it over base years.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.3.3 / Table 3.3.3.
"""
return col(risk_standardized) * col(trend_to_by3) * col(base_year_weight)
# ── §3.2.5 ────────────────────────────────────────────────────────────────
def voluntary_guardrail(
voluntary_historical_pbpm: IntoExpr,
claims_historical_pbpm: IntoExpr,
rate: float = VOLUNTARY_GUARDRAIL_RATE,
) -> nw.Expr:
"""The voluntary Historical Benchmark held within ±10 percent of the
claims-aligned one; null when either is null.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.2.5.
"""
claims = col(claims_historical_pbpm)
return nw.when(~claims.is_null()).then(
col(voluntary_historical_pbpm).clip(
lower_bound=claims * (1.0 - rate), upper_bound=claims * (1.0 + rate)
)
)
def voluntary_guardrail_status(
voluntary_historical_pbpm: IntoExpr,
claims_historical_pbpm: IntoExpr,
rate: float = VOLUNTARY_GUARDRAIL_RATE,
) -> nw.Expr:
"""Which side of the guardrail the voluntary benchmark landed on; null
when either input is null.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.2.5.
"""
voluntary, claims = col(voluntary_historical_pbpm), col(claims_historical_pbpm)
return (
nw.when(voluntary.is_null() | claims.is_null())
.then(nw.lit(None, dtype=nw.String()))
.when(voluntary > claims * (1.0 + rate))
.then(nw.lit(CAPPED_AT_UPPER))
.when(voluntary < claims * (1.0 - rate))
.then(nw.lit(RAISED_TO_LOWER))
.otherwise(nw.lit(WITHIN_GUARDRAIL))
)
def contributes_to_voluntary_benchmark(
category: IntoExpr, by3_category: IntoExpr
) -> nw.Expr:
"""A voluntarily aligned beneficiary contributes unless High Needs in
the PY without being High Needs in BY 3 (a null BY 3 category is not
High Needs).
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.2.5.
"""
was_high_needs = (col(by3_category) == HIGH_NEEDS).fill_null(False)
return (col(category) != HIGH_NEEDS) | was_high_needs
# ── §3.4 ──────────────────────────────────────────────────────────────────
def regional_efficiency_eligible(
spending_designation: IntoExpr, risk_option: IntoExpr
) -> nw.Expr:
"""Lower-spending ACOs in the Global Risk track.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.4.3.
"""
return (col(spending_designation) == LOWER_SPENDING) & (col(risk_option) == GLOBAL)
def uncapped_regional_efficiency_adjustment(
regional_ffs_pbpm: IntoExpr, historical_benchmark_pbpm: IntoExpr
) -> nw.Expr:
"""50 percent of the positive regional − historical difference.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.4.3 / Table 3.4.3.1.
"""
difference = col(regional_ffs_pbpm) - col(historical_benchmark_pbpm)
return difference.clip(lower_bound=0.0) * REGIONAL_EFFICIENCY_SHARE
def benchmark_adjustment_cap(
uspcc_amount: IntoExpr, py_average_risk_score: IntoExpr, cap_rate: IntoExpr | float
) -> nw.Expr:
"""USPCC-based amount × PY average risk score × cap rate.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.4.3 / Table 3.4.3.3.
"""
return col(uspcc_amount) * col(py_average_risk_score) * lit_or_col(cap_rate)
def regional_efficiency_adjustment(
regional_ffs_pbpm: IntoExpr,
historical_benchmark_pbpm: IntoExpr,
*,
eligible: IntoExpr,
uspcc_amount: IntoExpr,
py_average_risk_score: IntoExpr,
cap_rate: IntoExpr | float,
) -> nw.Expr:
"""Applied REA: zero when ineligible or not positive, else the lesser
of the uncapped REA and the cap; null when a needed input is null (a
positive REA never falls back to its uncapped amount).
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.4.3 / Tables
3.4.3.1–3.4.3.4.
"""
uncapped = uncapped_regional_efficiency_adjustment(
regional_ffs_pbpm, historical_benchmark_pbpm
)
cap = benchmark_adjustment_cap(uspcc_amount, py_average_risk_score, cap_rate)
flag = col(eligible)
return (
nw.when(uncapped.is_null() | flag.is_null())
.then(_NULL)
.when(~flag | (uncapped == 0))
.then(nw.lit(0.0))
.when(cap.is_null())
.then(_NULL)
.otherwise(nw.min_horizontal(uncapped, cap))
)
# ── §3.5 ──────────────────────────────────────────────────────────────────
def _guardrail(performance_year: IntoExpr | int, side: int) -> nw.Expr:
year = lit_or_col(performance_year)
bound = None
for model_year, bounds in ACPT_GUARDRAILS.items():
condition = year == FIRST_PERFORMANCE_YEAR + model_year - 1
bound = (nw.when if bound is None else bound.when)(condition).then(
nw.lit(bounds[side])
)
return bound.otherwise(_NULL)
def three_way_blended_trend(
two_way_trend: IntoExpr, acpt: IntoExpr, performance_year: IntoExpr | int
) -> nw.Expr:
"""Two-thirds two-way trend + one-third ACPT, held within the PY's
guardrail around the two-way trend; null for a PY outside the model.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5.1 / Table 3.5.1.
"""
two_way = col(two_way_trend)
raw = (1.0 - ACPT_WEIGHT) * two_way + ACPT_WEIGHT * col(acpt)
lower = two_way + _guardrail(performance_year, 0)
upper = two_way + _guardrail(performance_year, 1)
return nw.when(~lower.is_null()).then(
raw.clip(lower_bound=lower, upper_bound=upper)
)
def risk_score_growth_cap(category: IntoExpr) -> nw.Expr:
"""The category's growth cap: 3 percent A&D and ESRD, 4 percent High
Needs; null for anything else.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5.2.
"""
return col(category).replace_strict(
dict(RISK_SCORE_GROWTH_CAPS), default=None, return_dtype=nw.Float64()
)
def capped_risk_score(
by3_risk_score: IntoExpr, py_risk_score: IntoExpr, category: IntoExpr
) -> nw.Expr:
"""PY risk score held within BY 3 × (1 ± cap). Null without a BY 3
score or a known category: the uncapped score never passes through.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5.2.
"""
by3, cap = col(by3_risk_score), risk_score_growth_cap(category)
return nw.when(~by3.is_null() & ~cap.is_null()).then(
col(py_risk_score).clip(
lower_bound=by3 * (1.0 - cap), upper_bound=by3 * (1.0 + cap)
)
)
def risk_growth_cap_status(
by3_risk_score: IntoExpr, py_risk_score: IntoExpr, category: IntoExpr
) -> nw.Expr:
"""Whether the PY risk score was capped, raised to the floor, kept, or
could not be capped for a missing reference.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5.2.
"""
by3, py, cap = (
col(by3_risk_score),
col(py_risk_score),
risk_score_growth_cap(category),
)
return (
nw.when(by3.is_null() | py.is_null() | cap.is_null())
.then(nw.lit(RISK_CAP_REFERENCE_MISSING))
.when(py > by3 * (1.0 + cap))
.then(nw.lit(CAPPED))
.when(py < by3 * (1.0 - cap))
.then(nw.lit(FLOORED))
.otherwise(nw.lit(WITHIN_CAP))
)
def performance_year_benchmark_pbpm(
historical_benchmark_pbpm: IntoExpr,
benchmark_adjustment: IntoExpr,
trend_factor: IntoExpr,
capped_py_risk_score: IntoExpr,
) -> nw.Expr:
"""(Historical Benchmark + REA or PSA) × three-way trend × capped PY
risk score; a null adjustment stays null.
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5 / §3.5.2.
"""
adjusted = col(historical_benchmark_pbpm) + col(benchmark_adjustment)
return adjusted * col(trend_factor) * col(capped_py_risk_score)
def benchmark_dollars(
py_benchmark_pbpm: IntoExpr,
full_year_months: IntoExpr,
hybrid_months_and_factors: Sequence[tuple[IntoExpr, IntoExpr]] = (),
) -> nw.Expr:
"""cents(PBPM × [full-year months + Σ hybrid months × factor]).
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.5.3 / Table 3.5.3.2 /
Table 3.5.4 lines 8 and 22.
"""
hybrid = nw.lit(0.0)
for months, factor in hybrid_months_and_factors:
hybrid = hybrid + col(months) * col(factor)
return cents(col(py_benchmark_pbpm) * (col(full_year_months) + hybrid))
def month_weighted_mean(value: IntoExpr, months: IntoExpr) -> nw.Expr:
"""Aggregation: Σ value × months ÷ Σ months, over rows whose value is
known and months positive; null without such rows. Use inside
``group_by(...).agg(...)`` (average risk scores, the claims/voluntary
PBPM combination, regional FFS expenditure).
Source: LEAD PY2027 methodology (bib E4PEU3HV) §3.3.1 / §3.4.1 /
§3.5.2 / §3.5.4.
"""
amount, weight = col(value), col(months)
counted = ~amount.is_null() & (weight > 0)
numerator = nw.when(counted).then(amount * weight).otherwise(nw.lit(0.0)).sum()
denominator = nw.when(counted).then(weight).otherwise(nw.lit(0.0)).sum()
return ratio(numerator, denominator)