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.
101 lines
3.0 KiB
Python
101 lines
3.0 KiB
Python
"""lead.timing: the run-out, the settlement basis rule and settlement-stage
|
|
inputs."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
import pytest
|
|
|
|
from lead.timing import (
|
|
ACTUAL_CLAIMS_BASIS,
|
|
CMS_SETTLEMENT_REPORT,
|
|
CMS_SETTLEMENT_REPORT_MISSING,
|
|
COMPUTED_STATUS,
|
|
FINAL_NOT_YET_MADE,
|
|
PARTIAL_YEAR_STATUS,
|
|
PROJECTED_BASIS,
|
|
PROJECTED_STATUS,
|
|
PROVISIONAL_NOT_YET_MADE,
|
|
SettlementBasis,
|
|
require_projection_allowed,
|
|
settlement_basis_for_run,
|
|
settlement_runout_end,
|
|
settlement_stage_input,
|
|
validate_run_mode,
|
|
)
|
|
|
|
|
|
def test_runout_ends_march_31_of_the_following_year():
|
|
assert settlement_runout_end(2027) == date(2028, 3, 31)
|
|
|
|
|
|
def test_before_the_py_a_projection_is_projected():
|
|
basis = settlement_basis_for_run("projection", date(2026, 10, 20), 2027, 0)
|
|
assert basis == SettlementBasis(
|
|
PROJECTED_BASIS, PROJECTED_STATUS, PROJECTED_STATUS, date(2026, 10, 20)
|
|
)
|
|
|
|
|
|
def test_inside_the_py_rows_are_partial_actuals_under_a_projected_headline():
|
|
basis = settlement_basis_for_run("projection", date(2027, 6, 1), 2027, 10)
|
|
assert (basis.basis, basis.headline_status, basis.actual_rows_status) == (
|
|
PROJECTED_BASIS,
|
|
PROJECTED_STATUS,
|
|
PARTIAL_YEAR_STATUS,
|
|
)
|
|
|
|
|
|
def test_after_the_runout_rows_are_actual():
|
|
for mode in ("projection", "actual"):
|
|
basis = settlement_basis_for_run(mode, date(2028, 4, 1), 2027, 10)
|
|
assert basis.basis == ACTUAL_CLAIMS_BASIS
|
|
assert basis.headline_status == basis.actual_rows_status == COMPUTED_STATUS
|
|
|
|
|
|
def test_after_the_runout_without_rows_is_still_a_projection():
|
|
assert (
|
|
settlement_basis_for_run("rewind_test", date(2028, 4, 1), 2027, 0).basis
|
|
== PROJECTED_BASIS
|
|
)
|
|
|
|
|
|
def test_rows_before_the_py_are_a_feed_error():
|
|
with pytest.raises(ValueError, match="data from the future"):
|
|
settlement_basis_for_run("projection", date(2026, 10, 20), 2027, 3)
|
|
|
|
|
|
def test_actual_mode_before_the_runout_fails_naming_the_input():
|
|
for as_of, rows in ((date(2026, 10, 20), 0), (date(2027, 6, 1), 10)):
|
|
with pytest.raises(
|
|
ValueError, match="performance_year_claims_by_provider_class"
|
|
):
|
|
settlement_basis_for_run("actual", as_of, 2027, rows)
|
|
|
|
|
|
def test_run_modes_are_validated():
|
|
assert validate_run_mode("rewind_test") == "rewind_test"
|
|
with pytest.raises(ValueError, match="run_mode must be one of"):
|
|
validate_run_mode("Projection")
|
|
require_projection_allowed("projection", "x", "y")
|
|
|
|
|
|
def test_settlement_stage_inputs_follow_the_as_of_date():
|
|
start = date(2028, 1, 1)
|
|
assert settlement_stage_input(
|
|
date(2027, 12, 31), start, None, PROVISIONAL_NOT_YET_MADE
|
|
) == (
|
|
0.0,
|
|
PROVISIONAL_NOT_YET_MADE,
|
|
)
|
|
assert settlement_stage_input(start, start, None, FINAL_NOT_YET_MADE) == (
|
|
None,
|
|
CMS_SETTLEMENT_REPORT_MISSING,
|
|
)
|
|
assert settlement_stage_input(
|
|
start, start, 6_456_540, PROVISIONAL_NOT_YET_MADE
|
|
) == (
|
|
6_456_540.0,
|
|
CMS_SETTLEMENT_REPORT,
|
|
)
|