From 4c8614814a34602e5bc1146e50b2814095d22ff9 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Wed, 11 Nov 2026 23:55:45 -0500 Subject: [PATCH 1/7] Add New York CCAP differentials and enhanced payment rates Co-Authored-By: Claude Opus 5.5 --- .../ny-ccap-rate-differentials.added.md | 1 + .../ccap/differentials/accreditation.yaml | 14 + .../ocfs/ccap/differentials/homelessness.yaml | 14 + .../legally_exempt_homelessness.yaml | 14 + .../ny/ocfs/ccap/differentials/maximum.yaml | 12 + .../differentials/maximum_individual.yaml | 12 + .../ny/ocfs/ccap/differentials/minimum.yaml | 12 + .../differentials/monroe_accreditation.yaml | 12 + .../monroe_legally_exempt_homelessness.yaml | 12 + .../differentials/nontraditional_hours.yaml | 14 + .../nyc_nontraditional_hours.yaml | 11 + .../gov/states/ny/ocfs/ccap/rates/daily.yaml | 12 +- .../ocfs/ccap/rates/enhanced_group/both.yaml | 23 + .../ccap/rates/enhanced_group/single.yaml | 23 + .../states/ny/ocfs/ccap/rates/part_day.yaml | 12 +- .../ccap/rates/special_needs_multiplier.yaml | 9 + .../gov/states/ny/ocfs/ccap/rates/weekly.yaml | 12 +- policyengine_us/programs.yaml | 2 +- .../core/test_ny_ccap_rates_invariants.py | 346 ++++++++ .../ocfs/ccap/rates/ny_ccap_market_rate.yaml | 763 ++++++++++++++++++ .../ocfs/ccap/rates/ny_ccap_payment_rate.yaml | 497 ++++++++++++ policyengine_us/tools/ny_ccap.py | 54 ++ .../gov/states/ny/ocfs/ccap/ny_ccap.py | 22 +- .../ocfs/ccap/ny_ccap_childcare_expenses.py | 23 + .../ccap/rates/ny_ccap_accredited_provider.py | 19 + .../rates/ny_ccap_day_care_center_rate.py | 32 + .../ocfs/ccap/rates/ny_ccap_differential.py | 60 ++ .../ccap/rates/ny_ccap_enhanced_health.py | 19 + .../ccap/rates/ny_ccap_enhanced_training.py | 19 + .../ny/ocfs/ccap/rates/ny_ccap_market_rate.py | 80 +- .../rates/ny_ccap_nontraditional_hours.py | 20 + .../ocfs/ccap/rates/ny_ccap_payment_rate.py | 30 + .../ccap/rates/ny_ccap_regular_market_rate.py | 53 ++ .../ccap/rates/ny_ccap_special_needs_care.py | 21 + 34 files changed, 2187 insertions(+), 92 deletions(-) create mode 100644 changelog.d/ny-ccap-rate-differentials.added.md create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_accreditation.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_legally_exempt_homelessness.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nyc_nontraditional_hours.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/both.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/single.yaml create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/special_needs_multiplier.yaml create mode 100644 policyengine_us/tests/core/test_ny_ccap_rates_invariants.py create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml create mode 100644 policyengine_us/tools/ny_ccap.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap_childcare_expenses.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_accredited_provider.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_health.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_training.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_care.py diff --git a/changelog.d/ny-ccap-rate-differentials.added.md b/changelog.d/ny-ccap-rate-differentials.added.md new file mode 100644 index 00000000000..29a53295f1d --- /dev/null +++ b/changelog.d/ny-ccap-rate-differentials.added.md @@ -0,0 +1 @@ +Added New York CCAP payment differentials, the special-needs market rate, and New York City enhanced informal and legally exempt group rates under the October 2024 schedule. diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml new file mode 100644 index 00000000000..075d338fc22 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml @@ -0,0 +1,14 @@ +description: New York districts pay this default optional differential to nationally accredited licensed or registered providers. +# Use the October 2024 state minimum or optional zero where no district +# percentage has been confirmed. Separate parameters hold confirmed local rates. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0 +metadata: + unit: /1 + label: New York CCAP accreditation differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml new file mode 100644 index 00000000000..4b5c769efe4 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml @@ -0,0 +1,14 @@ +description: New York districts pay this default differential to licensed or registered providers caring for children experiencing homelessness. +# Use the October 2024 state minimum or optional zero where no district +# percentage has been confirmed. Separate parameters hold confirmed local rates. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0.05 +metadata: + unit: /1 + label: New York CCAP homelessness differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml new file mode 100644 index 00000000000..4a94637baff --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml @@ -0,0 +1,14 @@ +description: New York districts pay this default optional differential to legally exempt providers caring for children experiencing homelessness. +# Use the October 2024 state minimum or optional zero where no district +# percentage has been confirmed. Separate parameters hold confirmed local rates. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0 +metadata: + unit: /1 + label: New York CCAP legally exempt homelessness differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml new file mode 100644 index 00000000000..c954dc518cd --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml @@ -0,0 +1,12 @@ +description: New York limits combined differentials to this fraction of the lesser of actual cost and the applicable market rate unless OCFS grants a waiver. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0.25 +metadata: + unit: /1 + label: New York CCAP maximum differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml new file mode 100644 index 00000000000..59b226206b8 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml @@ -0,0 +1,12 @@ +description: New York limits each differential to this fraction of the lesser of actual cost and the applicable market rate. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0.15 +metadata: + unit: /1 + label: New York CCAP maximum individual differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml new file mode 100644 index 00000000000..d0a40c8d3d0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml @@ -0,0 +1,12 @@ +description: New York requires at least this differential for nontraditional hours and licensed or registered homelessness care, and for accreditation when a district elects it. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0.05 +metadata: + unit: /1 + label: New York CCAP minimum differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_accreditation.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_accreditation.yaml new file mode 100644 index 00000000000..57a990e7ae0 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_accreditation.yaml @@ -0,0 +1,12 @@ +description: Monroe County pays this optional accreditation differential under its approved Child and Family Services Plan. +# OCFS approved the plan effective November 18, 2024. The monthly model +# first reads that choice in December; earlier months use the state default. +values: + 2024-10-01: 0 + 2024-11-18: 0.1 +metadata: + unit: /1 + label: Monroe County CCAP accreditation differential + reference: + - title: Monroe County approved 2024-2029 Child and Family Services Plan, Appendix N + href: https://www.monroecounty.gov/files/hs/2024-2029%20Monroe_County_Child_Family_Services_Plan_Merged.pdf diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_legally_exempt_homelessness.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_legally_exempt_homelessness.yaml new file mode 100644 index 00000000000..00d9040bef7 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/monroe_legally_exempt_homelessness.yaml @@ -0,0 +1,12 @@ +description: Monroe County pays this optional legally exempt homelessness differential under its approved Child and Family Services Plan. +# OCFS approved the plan effective November 18, 2024. The monthly model +# first reads that choice in December; earlier months use the state default. +values: + 2024-10-01: 0 + 2024-11-18: 0.05 +metadata: + unit: /1 + label: Monroe County CCAP legally exempt homelessness differential + reference: + - title: Monroe County approved 2024-2029 Child and Family Services Plan, Appendix N + href: https://www.monroecounty.gov/files/hs/2024-2029%20Monroe_County_Child_Family_Services_Plan_Merged.pdf diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml new file mode 100644 index 00000000000..cf2637c795f --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml @@ -0,0 +1,14 @@ +description: New York districts pay this default differential for care during nontraditional hours. +# Use the October 2024 state minimum or optional zero where no district +# percentage has been confirmed. Separate parameters hold confirmed local rates. +# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +values: + 2024-10-01: 0.05 +metadata: + unit: /1 + label: New York CCAP nontraditional hours differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nyc_nontraditional_hours.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nyc_nontraditional_hours.yaml new file mode 100644 index 00000000000..02c9ebce275 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nyc_nontraditional_hours.yaml @@ -0,0 +1,11 @@ +description: New York City pays this differential for child care during nontraditional hours. +# City officials reported implementing 15% in September 2023. Apply that +# confirmed choice from the October 2024 market-rate schedule modeled here. +values: + 2024-10-01: 0.15 +metadata: + unit: /1 + label: New York City CCAP nontraditional hours differential + reference: + - title: New York City Council Committee on Education and Committee on Women and Gender Equity, September 21, 2023 + href: https://legistar.council.nyc.gov/View.ashx?GUID=43D12D7C-D5F5-4CDC-B300-422110951852&ID=12379758&M=F#page=22 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/daily.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/daily.yaml index be457ad47c0..816ebbead12 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/daily.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/daily.yaml @@ -182,15 +182,17 @@ GROUP_4: 2024-10-01: 42 SCHOOL_AGE: 2024-10-01: 39 + # Group 4 contains only New York City. ACS chose 75% of the + # registered family day care rate for enhanced informal providers. LE_ENH: INFANT: - 2024-10-01: 56 + 2024-10-01: 60 TODDLER: - 2024-10-01: 49 + 2024-10-01: 53 PRESCHOOLER: - 2024-10-01: 46 + 2024-10-01: 49 SCHOOL_AGE: - 2024-10-01: 42 + 2024-10-01: 45 metadata: propagate_metadata_to_children: true @@ -204,3 +206,5 @@ metadata: reference: - title: 24-OCFS-LCM-22 Attachment A, daily market rates for county Groups 1-4 (file pages 15-18), effective October 1, 2024 href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=15 + - title: NYC ACS FS-023, revised October 2024 + href: https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/both.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/both.yaml new file mode 100644 index 00000000000..189d121c78b --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/both.yaml @@ -0,0 +1,23 @@ +description: New York districts pay this fraction of the day care center market rate to legally exempt group child care programs meeting both the enhanced health requirement and the enhanced training requirement. +# New York City publishes enhanced rates in FS-023. Outside New York City, +# use the standard group rates because no district enhancement was confirmed. +# A zero fraction preserves the standard rate rather than creating an enhancement. +GROUP_1: + 2024-10-01: 0 +GROUP_2: + 2024-10-01: 0 +GROUP_3: + 2024-10-01: 0 +GROUP_4: + 2024-10-01: 0.87 + +metadata: + unit: /1 + label: New York CCAP legally exempt group health and training enhanced rate fraction + breakdown: + - ny_ccap_county_group + reference: + - title: 24-OCFS-LCM-22 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: NYC ACS FS-023, revised October 2024 + href: https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/single.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/single.yaml new file mode 100644 index 00000000000..90045a764cc --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/enhanced_group/single.yaml @@ -0,0 +1,23 @@ +description: New York districts pay this fraction of the day care center market rate to legally exempt group child care programs meeting either the enhanced health requirement or the enhanced training requirement. +# New York City publishes enhanced rates in FS-023. Outside New York City, +# use the standard group rates because no district enhancement was confirmed. +# A zero fraction preserves the standard rate rather than creating an enhancement. +GROUP_1: + 2024-10-01: 0 +GROUP_2: + 2024-10-01: 0 +GROUP_3: + 2024-10-01: 0 +GROUP_4: + 2024-10-01: 0.81 + +metadata: + unit: /1 + label: New York CCAP legally exempt group health or training enhanced rate fraction + breakdown: + - ny_ccap_county_group + reference: + - title: 24-OCFS-LCM-22 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: NYC ACS FS-023, revised October 2024 + href: https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/part_day.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/part_day.yaml index 26c8520cdf8..2f921f36d52 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/part_day.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/part_day.yaml @@ -182,15 +182,17 @@ GROUP_4: 2024-10-01: 28 SCHOOL_AGE: 2024-10-01: 26 + # Group 4 contains only New York City. ACS chose 75% of the + # registered family day care rate for enhanced informal providers. LE_ENH: INFANT: - 2024-10-01: 37 + 2024-10-01: 40 TODDLER: - 2024-10-01: 33 + 2024-10-01: 35 PRESCHOOLER: - 2024-10-01: 30 + 2024-10-01: 32 SCHOOL_AGE: - 2024-10-01: 28 + 2024-10-01: 30 metadata: propagate_metadata_to_children: true @@ -204,3 +206,5 @@ metadata: reference: - title: 24-OCFS-LCM-22 Attachment A, part-day market rates for county Groups 1-4 (file pages 15-18), effective October 1, 2024 href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=15 + - title: NYC ACS FS-023, revised October 2024 + href: https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/special_needs_multiplier.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/special_needs_multiplier.yaml new file mode 100644 index 00000000000..fcb1031b851 --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/special_needs_multiplier.yaml @@ -0,0 +1,9 @@ +description: New York pays this multiple of the regular market rate for qualifying children with special needs whose costs exceed the regular rate. +values: + 2024-10-01: 1.15 +metadata: + unit: /1 + label: New York CCAP special needs market rate multiplier + reference: + - title: 24-OCFS-LCM-22, market rates for qualifying children with special needs + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=7 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/weekly.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/weekly.yaml index aa49b0b0032..4c420badd5c 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/weekly.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/rates/weekly.yaml @@ -182,15 +182,17 @@ GROUP_4: 2024-10-01: 211 SCHOOL_AGE: 2024-10-01: 188 + # Group 4 contains only New York City. ACS chose 75% of the + # registered family day care rate for enhanced informal providers. LE_ENH: INFANT: - 2024-10-01: 280 + 2024-10-01: 300 TODDLER: - 2024-10-01: 245 + 2024-10-01: 263 PRESCHOOLER: - 2024-10-01: 228 + 2024-10-01: 244 SCHOOL_AGE: - 2024-10-01: 202 + 2024-10-01: 217 metadata: propagate_metadata_to_children: true @@ -204,3 +206,5 @@ metadata: reference: - title: 24-OCFS-LCM-22 Attachment A, weekly market rates for county Groups 1-4 (file pages 15-18), effective October 1, 2024 href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=15 + - title: NYC ACS FS-023, revised October 2024 + href: https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 0f74596fd68..4566283129f 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -875,7 +875,7 @@ programs: full_name: New York Child Care Assistance Program variable: ny_ccap parameter_prefix: gov.states.ny.ocfs.ccap - notes: Models New York income eligibility, market rates, and the family share, including the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. Not modeled - the mandatory non-traditional-hours and homelessness payment differentials required by 18 NYCRR 415.9(f)(2)-(3) and the discretionary accreditation differential, the special-needs substituted market rate under 415.9(h)(4), safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share + notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share, including the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. District percentages use the October 2024 state minimums or optional zeros where no local rate has been confirmed; later differential amendments remain outside this implementation. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share - state: OR status: complete name: Oregon ERDC diff --git a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py new file mode 100644 index 00000000000..2d9bff6a2e2 --- /dev/null +++ b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py @@ -0,0 +1,346 @@ +"""Check New York CCAP rate and payment bounds in batched populations. + +Hypothesis varies county groups, providers, age bands, schedules, expenses, +eligibility and family shares. Each household has twins for the payment +flags and special-needs expense boundaries. Published rate-period counts +supply the legacy reference without calling the new rate formulas. + +Python checks relationships across those twins and the person-to-SPM-unit +aggregation. YAML cases separately pin the published dollar amounts and +expense allocation. Simulations reuse the imported tax-benefit system; +three examples each calculate one population for January 2025. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system + +YEAR = 2025 +MONTH = "2025-01" +TOLERANCE = dict(atol=0.01, rtol=3e-7) +GROUPS = ("GROUP_1", "GROUP_2", "GROUP_3", "GROUP_4") +PROVIDERS = ("DCC_SACC", "FDC_GFDC", "LE_GC", "LE_STD", "LE_ENH") +AGES = ("INFANT", "TODDLER", "PRESCHOOLER", "SCHOOL_AGE") +# Each schedule records hours/day, days/week and the published rate periods. +# At 12 hours, each day earns a further part-day period. At 18 hours, +# each day earns a further daily period; days six and seven also earn one. +SCHEDULES = ( + (0, 0, (0, 0, 0)), + (3, 1, (0, 0, 1)), + (5, 7, (0, 0, 7)), + (6, 4, (0, 4, 0)), + (8, 3, (0, 3, 0)), + (6, 5, (1, 0, 0)), + (8, 7, (1, 2, 0)), + (12, 5, (1, 0, 5)), + (18, 7, (1, 9, 0)), +) +LEGACY_NYC_ENHANCED = { + "weekly": (280, 245, 228, 202), + "daily": (56, 49, 46, 42), + "part_day": (37, 33, 30, 28), +} +VARIANTS = ( + "default", + "nontraditional", + "homeless", + "accredited", + "differentials", + "health", + "training", + "enhancements", + "special", + "unqualified", + "at_regular", + "below_regular", + "special_differentials", +) +FLAGS = { + "ny_ccap_nontraditional_hours": { + "nontraditional", + "differentials", + "special_differentials", + }, + "ny_ccap_accredited_provider": { + "accredited", + "differentials", + "special_differentials", + }, + "ny_ccap_enhanced_health": {"health", "enhancements"}, + "ny_ccap_enhanced_training": {"training", "enhancements"}, + "ny_ccap_special_needs_care": { + "special", + "unqualified", + "at_regular", + "below_regular", + "special_differentials", + }, +} + + +@st.composite +def child(draw): + return { + "provider": draw(st.sampled_from(PROVIDERS)), + "age": draw(st.sampled_from(AGES)), + "schedule": draw(st.integers(0, len(SCHEDULES) - 1)), + "eligible": draw(st.booleans()), + "disabled": draw(st.booleans()), + } + + +@st.composite +def household(draw): + return { + "group": draw(st.sampled_from(GROUPS)), + "children": draw(st.lists(child(), min_size=1, max_size=2)), + "expenses": draw(st.integers(0, 12_000)), + "family_share": draw(st.integers(0, 20_000)), + } + + +def _coverage_households(): + # Every example includes all county/provider pairs and each schedule. + return [ + { + "group": group, + "children": [ + { + "provider": provider, + "age": AGES[(group_index + provider_index) % len(AGES)], + "schedule": (group_index * len(PROVIDERS) + provider_index) + % len(SCHEDULES), + "eligible": True, + "disabled": False, + } + ], + "expenses": 10_000, + "family_share": (0, 100, 20_000)[provider_index % 3], + } + for group_index, group in enumerate(GROUPS) + for provider_index, provider in enumerate(PROVIDERS) + ] + + +def _table_rate(parameters, group, provider, age, schedule, legacy=False): + counts = SCHEDULES[schedule][2] + total = 0 + for table_name, count in zip(("weekly", "daily", "part_day"), counts): + if legacy and group == "GROUP_4" and provider == "LE_ENH": + cell = LEGACY_NYC_ENHANCED[table_name][AGES.index(age)] + else: + cell = parameters.rates[table_name][group][provider][age] + total += count * cell + # The model converts weekly schedules using 52 weeks and 12 months. + return total * 52 / 12 + + +def _simulate(households, parameters): + situation = { + entity: {} + for entity in ( + "people", + "tax_units", + "marital_units", + "families", + "spm_units", + "households", + ) + } + rows = [] + for household_index, unit in enumerate(households): + for variant in VARIANTS: + unit_name = f"unit_{household_index}_{variant}" + members = [] + child_rows = [] + for index, case in enumerate(unit["children"]): + name = f"{unit_name}_child_{index}" + members.append(name) + regular = _table_rate( + parameters, + unit["group"], + case["provider"], + case["age"], + case["schedule"], + ) + expense = unit["expenses"] + if variant == "at_regular": + expense = regular + elif variant == "below_regular": + expense = regular * 0.75 + disabled = case["disabled"] + if variant in { + "special", + "at_regular", + "below_regular", + "special_differentials", + }: + disabled = True + elif variant == "unqualified": + disabled = False + hours, days, _ = SCHEDULES[case["schedule"]] + person = { + "age": {YEAR: (1, 2, 4, 8)[AGES.index(case["age"])]}, + "is_disabled": {YEAR: disabled}, + "childcare_provider_type_group": {YEAR: case["provider"]}, + "ny_ccap_age_group": {YEAR: case["age"]}, + "childcare_hours_per_day": {YEAR: hours}, + "childcare_days_per_week": {YEAR: days}, + "ny_ccap_eligible_child": {MONTH: case["eligible"]}, + "ny_ccap_childcare_expenses": {MONTH: expense}, + } + if variant == "at_regular": + # Store both sides through the same float32 input path. + person["ny_ccap_regular_market_rate"] = {MONTH: regular} + # Omit false inputs so the default twins check their defaults. + for flag, variants in FLAGS.items(): + if variant in variants: + person[flag] = {YEAR: True} + person_index = len(situation["people"]) + situation["people"][name] = person + situation["marital_units"][name] = {"members": [name]} + child_rows.append( + { + **case, + "person_index": person_index, + "expense": expense, + "regular": regular, + "legacy": _table_rate( + parameters, + unit["group"], + case["provider"], + case["age"], + case["schedule"], + legacy=True, + ), + } + ) + for entity in ("tax_units", "families", "spm_units"): + situation[entity][unit_name] = {"members": members} + situation["spm_units"][unit_name].update( + { + "ny_ccap_family_share": {MONTH: unit["family_share"]}, + "spm_unit_pre_subsidy_childcare_expenses": { + YEAR: unit["expenses"] + * 12 + * sum( + SCHEDULES[case["schedule"]][0] > 0 + for case in unit["children"] + ) + }, + } + ) + situation["households"][unit_name] = { + "members": members, + "state_code": {YEAR: "NY"}, + "ny_ccap_county_group": {YEAR: unit["group"]}, + "is_homeless": { + YEAR: variant + in {"homeless", "differentials", "special_differentials"} + }, + } + rows.append({**unit, "variant": variant, "children": child_rows}) + return Simulation(situation=situation, tax_benefit_system=system), rows + + +@settings( + max_examples=3, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) +@given(st.lists(household(), min_size=3, max_size=8)) +def test_ny_ccap_rate_and_payment_invariants(generated): + parameters = system.parameters(f"{MONTH}-01").gov.states.ny.ocfs.ccap + simulation, rows = _simulate(_coverage_households() + generated, parameters) + + def calc(variable, period=MONTH): + return np.asarray(simulation.calculate(variable, period), dtype=float) + + regular = calc("ny_ccap_regular_market_rate") + market = calc("ny_ccap_market_rate") + expenses = calc("ny_ccap_childcare_expenses") + payment = calc("ny_ccap_payment_rate") + center = calc("ny_ccap_day_care_center_rate") + differential = calc("ny_ccap_differential") + benefit = calc("ny_ccap") + default_indices = [ + case["person_index"] + for row in rows + if row["variant"] == "default" + for case in row["children"] + ] + for flag in FLAGS: + assert not calc(flag, YEAR)[default_indices].any() + assert np.isfinite(benefit).all() and (benefit >= 0).all() + assert (payment >= 0).all() + assert ((differential >= 0) & (differential <= 0.25 + 1e-7)).all() + # Every payment stays within the ordinary 25% differential ceiling. + assert (payment <= np.minimum(expenses, market) * 1.25 + 0.01).all() + + for row_index, row in enumerate(rows): + indices = [case["person_index"] for case in row["children"]] + counted = np.array( + [ + case["eligible"] and SCHEDULES[case["schedule"]][0] > 0 + for case in row["children"] + ] + ) + np.testing.assert_allclose( + benefit[row_index], + max(payment[indices][counted].sum() - row["family_share"], 0), + **TOLERANCE, + ) + for case in row["children"]: + i = case["person_index"] + if case["provider"] == "LE_GC": + assert payment[i] <= center[i] + 0.01 + if row["variant"] == "default": + if row["group"] == "GROUP_4" and case["provider"] == "LE_ENH": + # ACS's 75% informal rate intentionally exceeds main's 70%. + assert market[i] >= case["legacy"] - 0.01 + else: + np.testing.assert_allclose(market[i], case["legacy"], **TOLERANCE) + np.testing.assert_allclose(regular[i], case["regular"], **TOLERANCE) + if row["variant"] in {"special", "special_differentials"}: + expected = regular[i] * (1.15 if expenses[i] > regular[i] else 1) + np.testing.assert_allclose(market[i], expected, **TOLERANCE) + elif row["variant"] in {"unqualified", "at_regular", "below_regular"}: + np.testing.assert_allclose(market[i], regular[i], **TOLERANCE) + if row["variant"] == "default": + legacy_payment = sum( + min(case["expense"], case["legacy"]) + for case, eligible in zip(row["children"], counted) + if eligible + ) + legacy_benefit = max(legacy_payment - row["family_share"], 0) + has_nyc_enhanced = row["group"] == "GROUP_4" and any( + case["provider"] == "LE_ENH" for case in row["children"] + ) + if has_nyc_enhanced: + assert benefit[row_index] >= legacy_benefit - 0.01 + else: + np.testing.assert_allclose( + benefit[row_index], legacy_benefit, **TOLERANCE + ) + + # Each twin uses the same expenses, care and family share as its default. + for start in range(0, len(rows), len(VARIANTS)): + base = rows[start] + base_indices = [case["person_index"] for case in base["children"]] + for variant in ( + "nontraditional", + "homeless", + "accredited", + "differentials", + "health", + "training", + "enhancements", + ): + position = start + VARIANTS.index(variant) + indices = [case["person_index"] for case in rows[position]["children"]] + assert (payment[indices] >= payment[base_indices] - 0.01).all() + assert benefit[position] >= benefit[start] - 0.01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.yaml index 50e1131d4de..3957d5b52b3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.yaml @@ -196,3 +196,766 @@ output: ny_ccap_market_rate: 0 absolute_error_margin: 0.01 + +# ACS FS-023 pays $300 per weekly period. +# One such period per week gives $300 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published weekly rate for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 300 * 52 / 12 + ny_ccap_market_rate: 300 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $263 per weekly period. +# One such period per week gives $263 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published weekly rate for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 263 * 52 / 12 + ny_ccap_market_rate: 263 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $244 per weekly period. +# One such period per week gives $244 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published weekly rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 244 * 52 / 12 + ny_ccap_market_rate: 244 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $217 per weekly period. +# One such period per week gives $217 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published weekly rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 217 * 52 / 12 + ny_ccap_market_rate: 217 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $60 per daily period. +# One such period per week gives $60 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published daily rate for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 60 * 52 / 12 + ny_ccap_market_rate: 60 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $53 per daily period. +# One such period per week gives $53 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published daily rate for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 53 * 52 / 12 + ny_ccap_market_rate: 53 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $49 per daily period. +# One such period per week gives $49 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published daily rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 49 * 52 / 12 + ny_ccap_market_rate: 49 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $45 per daily period. +# One such period per week gives $45 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published daily rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 45 * 52 / 12 + ny_ccap_market_rate: 45 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $40 per part-day period. +# One such period per week gives $40 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published part-day rate for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 40 * 52 / 12 + ny_ccap_market_rate: 40 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $35 per part-day period. +# One such period per week gives $35 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published part-day rate for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 35 * 52 / 12 + ny_ccap_market_rate: 35 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $32 per part-day period. +# One such period per week gives $32 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published part-day rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 32 * 52 / 12 + ny_ccap_market_rate: 32 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $30 per part-day period. +# One such period per week gives $30 * 52 / 12 monthly. +- name: NYC enhanced informal care pays the published part-day rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_ENH + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + output: + ny_ccap_regular_market_rate: 30 * 52 / 12 + ny_ccap_market_rate: 30 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $356 per weekly period with health. +# One such period per week gives $356 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly health rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 356 * 52 / 12 + ny_ccap_market_rate: 356 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $263 per weekly period with health. +# One such period per week gives $263 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly health rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 263 * 52 / 12 + ny_ccap_market_rate: 263 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $73 per daily period with health. +# One such period per week gives $73 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily health rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 73 * 52 / 12 + ny_ccap_market_rate: 73 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $56 per daily period with health. +# One such period per week gives $56 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily health rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 56 * 52 / 12 + ny_ccap_market_rate: 56 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $49 per part-day period with health. +# One such period per week gives $49 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day health rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 49 * 52 / 12 + ny_ccap_market_rate: 49 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $37 per part-day period with health. +# One such period per week gives $37 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day health rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + output: + ny_ccap_regular_market_rate: 37 * 52 / 12 + ny_ccap_market_rate: 37 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $356 per weekly period with training. +# One such period per week gives $356 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 356 * 52 / 12 + ny_ccap_market_rate: 356 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $263 per weekly period with training. +# One such period per week gives $263 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 263 * 52 / 12 + ny_ccap_market_rate: 263 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $73 per daily period with training. +# One such period per week gives $73 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 73 * 52 / 12 + ny_ccap_market_rate: 73 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $56 per daily period with training. +# One such period per week gives $56 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 56 * 52 / 12 + ny_ccap_market_rate: 56 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $49 per part-day period with training. +# One such period per week gives $49 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 49 * 52 / 12 + ny_ccap_market_rate: 49 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $37 per part-day period with training. +# One such period per week gives $37 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 37 * 52 / 12 + ny_ccap_market_rate: 37 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $382 per weekly period with health and training. +# One such period per week gives $382 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly health and training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 382 * 52 / 12 + ny_ccap_market_rate: 382 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $283 per weekly period with health and training. +# One such period per week gives $283 * 52 / 12 monthly. +- name: NYC exempt group care receives the weekly health and training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 283 * 52 / 12 + ny_ccap_market_rate: 283 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $78 per daily period with health and training. +# One such period per week gives $78 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily health and training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 78 * 52 / 12 + ny_ccap_market_rate: 78 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $60 per daily period with health and training. +# One such period per week gives $60 * 52 / 12 monthly. +- name: NYC exempt group care receives the daily health and training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 60 * 52 / 12 + ny_ccap_market_rate: 60 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $52 per part-day period with health and training. +# One such period per week gives $52 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day health and training rate for preschooler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 52 * 52 / 12 + ny_ccap_market_rate: 52 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 pays $40 per part-day period with health and training. +# One such period per week gives $40 * 52 / 12 monthly. +- name: NYC exempt group care receives the part-day health and training rate for school-age care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: SCHOOL_AGE + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 40 * 52 / 12 + ny_ccap_market_rate: 40 * 52 / 12 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group weekly care pays zero for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group weekly care pays zero for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group daily care pays zero for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group daily care pays zero for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group part-day care pays zero for infant care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: INFANT + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# ACS FS-023 publishes $0 for exempt group care under age three. +- name: NYC enhanced exempt group part-day care pays zero for toddler care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: TODDLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 0 + ny_ccap_market_rate: 0 + absolute_error_margin: 0.01 + +# Outside NYC the state default retains the standard exempt group rate. +# Group 1 preschool care pays $308 * 52 / 12 monthly for one period weekly. +- name: Enhanced health and training retain the standard exempt group weekly rate outside NYC. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 308 * 52 / 12 + ny_ccap_market_rate: 308 * 52 / 12 + absolute_error_margin: 0.01 + +# Outside NYC the state default retains the standard exempt group rate. +# Group 1 preschool care pays $64 * 52 / 12 monthly for one period weekly. +- name: Enhanced health and training retain the standard exempt group daily rate outside NYC. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: DAILY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 64 * 52 / 12 + ny_ccap_market_rate: 64 * 52 / 12 + absolute_error_margin: 0.01 + +# Outside NYC the state default retains the standard exempt group rate. +# Group 1 preschool care pays $43 * 52 / 12 monthly for one period weekly. +- name: Enhanced health and training retain the standard exempt group part-day rate outside NYC. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: PART_DAY + childcare_days_per_week: 1 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_regular_market_rate: 43 * 52 / 12 + ny_ccap_market_rate: 43 * 52 / 12 + absolute_error_margin: 0.01 + +# $900 expenses do not satisfy every condition for the $1,150 rate. +- name: Costs below the regular rate retain the regular market rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_regular_market_rate: 1_000 + ny_ccap_childcare_expenses: 900 + is_disabled: true + ny_ccap_special_needs_care: true + output: + ny_ccap_market_rate: 1_000 + absolute_error_margin: 0.01 + +# $1,000 expenses do not satisfy every condition for the $1,150 rate. +- name: Costs equal to the regular rate retain the regular market rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_regular_market_rate: 1_000 + ny_ccap_childcare_expenses: 1_000 + is_disabled: true + ny_ccap_special_needs_care: true + output: + ny_ccap_market_rate: 1_000 + absolute_error_margin: 0.01 + +# $1,000 * 1.15 = $1,150. +- name: Qualifying needs and costs above the regular rate receive fifteen percent more. + period: 2025-01 + input: + state_code: NY + ny_ccap_regular_market_rate: 1_000 + ny_ccap_childcare_expenses: 1_001 + is_disabled: true + ny_ccap_special_needs_care: true + output: + ny_ccap_market_rate: 1_150 + absolute_error_margin: 0.01 + +# $2,000 expenses do not satisfy every condition for the $1,150 rate. +- name: A child without a disability retains the regular rate at the same high cost. + period: 2025-01 + input: + state_code: NY + ny_ccap_regular_market_rate: 1_000 + ny_ccap_childcare_expenses: 2_000 + is_disabled: false + ny_ccap_special_needs_care: true + output: + ny_ccap_market_rate: 1_000 + absolute_error_margin: 0.01 + +# $2,000 expenses do not satisfy every condition for the $1,150 rate. +- name: A parent who has not shown the additional care need retains the regular rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_regular_market_rate: 1_000 + ny_ccap_childcare_expenses: 2_000 + is_disabled: true + ny_ccap_special_needs_care: false + output: + ny_ccap_market_rate: 1_000 + absolute_error_margin: 0.01 + +# Six eight-hour days earn the enhanced $382 weekly rate and $78 daily rate. +# ($382 + $78) * 52 / 12 = $1,993.33 monthly. +- name: NYC enhanced group care prices an excess day at its enhanced daily rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + childcare_hours_per_day: 8 + childcare_days_per_week: 6 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_duration_of_care: WEEKLY + ny_ccap_regular_market_rate: (382 + 78) * 52 / 12 + ny_ccap_market_rate: (382 + 78) * 52 / 12 + absolute_error_margin: 0.01 + +# Five sixteen-hour days earn $382 plus five enhanced $52 part-day periods. +# ($382 + 5 * $52) * 52 / 12 = $2,782 monthly. +- name: NYC enhanced group care prices excess hours at its enhanced part-day rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + childcare_hours_per_day: 16 + childcare_days_per_week: 5 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + output: + ny_ccap_duration_of_care: WEEKLY + ny_ccap_regular_market_rate: (382 + 5 * 52) * 52 / 12 + ny_ccap_market_rate: (382 + 5 * 52) * 52 / 12 + absolute_error_margin: 0.01 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml new file mode 100644 index 00000000000..842eb1e2d33 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml @@ -0,0 +1,497 @@ +# 24-OCFS-LCM-22 applies each differential to min(expenses, market rate). +# $800 * 1.05 = $840, so the district can pay more than the $800 charge. +- name: Nontraditional hours raise payment above a charge below the market rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 800 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 840 + absolute_error_margin: 0.01 + +# min($1,200, $1,000) * 1.05 = $1,050. +- name: Homelessness raises payment for licensed center care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + is_homeless: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# Registered family providers receive the same mandatory homelessness rate. +# min($1,200, $1,000) * 1.05 = $1,050. +- name: Homelessness raises payment for registered family care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: FDC_GFDC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + is_homeless: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# The state default pays no optional accreditation differential. +- name: Accreditation alone leaves payment unchanged under the state default. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + output: + ny_ccap_differential: 0 + ny_ccap_payment_rate: 1_000 + absolute_error_margin: 0.01 + +# A district that elects a 5% accreditation differential pays $1,000 * 1.05. +- name: Accreditation raises payment when the district elects the differential. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# The district adds three 5% differentials: $1,000 * (1 + .05 * 3) = $1,150. +- name: A licensed provider receives all three differentials together. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + is_homeless: true + output: + ny_ccap_differential: 0.15 + ny_ccap_payment_rate: 1_150 + absolute_error_margin: 0.01 + +# min(.15 + .15 + .15, .25) = .25; $1,000 * 1.25 = $1,250. +- name: The total differential stops at twenty-five percent without a waiver. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: FDC_GFDC + ny_ccap_childcare_expenses: 2_000 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + is_homeless: true + output: + ny_ccap_differential: 0.25 + ny_ccap_payment_rate: 1_250 + absolute_error_margin: 0.01 + +# The state default pays no optional homelessness differential to exempt care. +- name: Homelessness leaves standard exempt informal care unchanged by default. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + is_homeless: true + output: + ny_ccap_differential: 0 + ny_ccap_payment_rate: 1_000 + absolute_error_margin: 0.01 + +# The nontraditional-hours differential includes exempt providers: +# min($1,200, $1,000) * 1.05 = $1,050. +- name: Nontraditional hours raise payment for enhanced exempt informal care. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_ENH + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# The district may elect an exempt-provider homelessness rate below 5%. +# $1,000 * 1.02 = $1,020. +- name: Exempt care receives an optional homelessness rate without a minimum. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + is_homeless: true + output: + ny_ccap_differential: 0.02 + ny_ccap_payment_rate: 1_020 + absolute_error_margin: 0.01 + +# Exempt providers never receive the accreditation differential. +- name: Accreditation cannot raise payment for exempt informal care. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + output: + ny_ccap_differential: 0 + ny_ccap_payment_rate: 1_000 + absolute_error_margin: 0.01 + +# NYC's enhanced group rate pays $382 weekly for both requirements. +# min($3,000, $382 * 52 / 12) * 1.25 exceeds $439 * 52 / 12, +# so the district pays the center rate, $1,902.33 monthly. +- name: Exempt group payment stops at the center rate after differentials. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + ny_ccap_childcare_expenses: 3_000 + ny_ccap_nontraditional_hours: true + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + is_homeless: true + output: + ny_ccap_market_rate: 382 * 52 / 12 + ny_ccap_differential: 0.25 + ny_ccap_day_care_center_rate: 439 * 52 / 12 + ny_ccap_payment_rate: 439 * 52 / 12 + absolute_error_margin: 0.01 + +# For a qualifying child, $1,000 * 1.15 = $1,150 becomes the market rate. +# The district then pays min($2,000, $1,150) * 1.05 = $1,207.50. +- name: Differentials apply after the special-needs market rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 2_000 + ny_ccap_regular_market_rate: 1_000 + is_disabled: true + ny_ccap_special_needs_care: true + ny_ccap_nontraditional_hours: true + output: + ny_ccap_market_rate: 1_150 + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_207.5 + absolute_error_margin: 0.01 + +# Actual expenses remain the differential base when they fall between the +# regular and special-needs rates: min($1,100, $1,150) * 1.05 = $1,155. +- name: The district applies differentials to costs below the special-needs rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_100 + ny_ccap_regular_market_rate: 1_000 + is_disabled: true + ny_ccap_special_needs_care: true + ny_ccap_nontraditional_hours: true + output: + ny_ccap_market_rate: 1_150 + ny_ccap_payment_rate: 1_155 + absolute_error_margin: 0.01 + +# Annual expenses of $12,000 give $1,000 monthly. After the 5% differential +# and one $100 family share, the family receives $1,000 * 1.05 - $100 = $950. +- name: The family receives the differential before subtracting one family share. + period: 2025-01 + input: + people: + child: + ny_ccap_eligible_child: true + childcare_provider_type_group: DCC_SACC + ny_ccap_market_rate: 1_200 + childcare_hours_per_day: 8 + ny_ccap_nontraditional_hours: true + spm_units: + unit: + members: [child] + spm_unit_pre_subsidy_childcare_expenses: 12_000 + ny_ccap_family_share: 100 + households: + household: + members: [child] + state_code: NY + ny_ccap_county_group: GROUP_1 + output: + ny_ccap_childcare_expenses: 1_000 + ny_ccap_payment_rate: 1_050 + ny_ccap: 950 + absolute_error_margin: 0.01 + +# Both children attend paid care, so each keeps $12,000 / 12 / 2 = $500. +# Only the eligible child receives $500 * 1.05 = $525. +- name: An ineligible sibling keeps their expense share when a differential applies. + period: 2025-01 + input: + people: + child: + ny_ccap_eligible_child: true + childcare_provider_type_group: DCC_SACC + ny_ccap_market_rate: 1_200 + childcare_hours_per_day: 8 + ny_ccap_nontraditional_hours: true + sibling: + ny_ccap_eligible_child: false + childcare_provider_type_group: DCC_SACC + ny_ccap_market_rate: 1_200 + childcare_hours_per_day: 8 + spm_units: + unit: + members: [child, sibling] + spm_unit_pre_subsidy_childcare_expenses: 12_000 + ny_ccap_family_share: 0 + households: + household: + members: [child, sibling] + state_code: NY + ny_ccap_county_group: GROUP_1 + output: + ny_ccap_childcare_expenses: [500, 500] + ny_ccap_payment_rate: [525, 500] + ny_ccap: 525 + absolute_error_margin: 0.01 + +# $1,000 * 1.05 - $2,000 falls below zero; the district pays $0. +- name: A family share above care plus the differential cannot produce a negative benefit. + period: 2025-01 + input: + people: + child: + ny_ccap_eligible_child: true + childcare_provider_type_group: DCC_SACC + ny_ccap_market_rate: 1_200 + childcare_hours_per_day: 8 + ny_ccap_nontraditional_hours: true + spm_units: + unit: + members: [child] + spm_unit_pre_subsidy_childcare_expenses: 12_000 + ny_ccap_family_share: 2_000 + households: + household: + members: [child] + state_code: NY + ny_ccap_county_group: GROUP_1 + output: + ny_ccap: 0 + absolute_error_margin: 0.01 + +# NYC elects 15% for nontraditional hours: $1,000 * 1.15 = $1,150. +- name: NYC pays its elected nontraditional-hours differential. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + output: + ny_ccap_differential: 0.15 + ny_ccap_payment_rate: 1_150 + absolute_error_margin: 0.01 + +# Monroe County elects 10% for accreditation: $1,000 * 1.10 = $1,100. +- name: Monroe County pays its elected accreditation differential. + period: 2025-01 + input: + state_code: NY + county_str: MONROE_COUNTY_NY + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + output: + ny_ccap_differential: 0.10 + ny_ccap_payment_rate: 1_100 + absolute_error_margin: 0.01 + +# Monroe County elects 5% for exempt care of homeless children: +# $1,000 * 1.05 = $1,050. +- name: Monroe County pays its elected exempt-provider homelessness differential. + period: 2025-01 + input: + state_code: NY + county_str: MONROE_COUNTY_NY + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + is_homeless: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# A district must pay at least 5% for nontraditional hours even if its +# configured percentage falls below the minimum: $1,000 * 1.05 = $1,050. +- name: The nontraditional-hours differential enforces the five-percent minimum. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# A district that elects an accreditation differential must pay at least 5%. +# max(.01, .05) = .05; $1,000 * 1.05 = $1,050. +- name: An elected accreditation differential enforces the five-percent minimum. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + output: + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 + absolute_error_margin: 0.01 + +# Each configured 30% differential stops at 15%; their sum stops at 25%. +# min(.15 + .15 + .15, .25) = .25; $1,000 * 1.25 = $1,250. +- name: Individual and total maxima limit excessive configured differentials. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.30 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.30 + people: + nontraditional: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + homeless: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + accredited: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + all_three: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + households: + nontraditional: + members: [nontraditional] + state_code: NY + ny_ccap_county_group: GROUP_1 + homeless: + members: [homeless] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + accredited: + members: [accredited] + state_code: NY + ny_ccap_county_group: GROUP_1 + all_three: + members: [all_three] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + output: + ny_ccap_differential: [0.15, 0.15, 0.15, 0.25] + ny_ccap_payment_rate: [1_150, 1_150, 1_150, 1_250] + absolute_error_margin: 0.01 + +# An OCFS waiver can raise the district's combined limit. With a 35% waived +# limit, min(.15 + .15 + .15, .35) = .35 and $1,000 * 1.35 = $1,350. +- name: An OCFS waiver allows the district to raise the combined limit. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.35 + state_code: NY + ny_ccap_county_group: GROUP_1 + childcare_provider_type_group: FDC_GFDC + ny_ccap_childcare_expenses: 2_000 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + is_homeless: true + output: + ny_ccap_differential: 0.35 + ny_ccap_payment_rate: 1_350 + absolute_error_margin: 0.01 diff --git a/policyengine_us/tools/ny_ccap.py b/policyengine_us/tools/ny_ccap.py new file mode 100644 index 00000000000..ec0b0292385 --- /dev/null +++ b/policyengine_us/tools/ny_ccap.py @@ -0,0 +1,54 @@ +from policyengine_us.model_api import * + + +def ny_ccap_monthly_rate( + person, period, parameters, weekly_rate, daily_rate, part_day_rate +): + """Price the scheduled and excess care periods under 24-OCFS-LCM-22.""" + p = parameters(period).gov.states.ny.ocfs.ccap + duration = person("ny_ccap_duration_of_care", period.this_year) + durations = duration.possible_values + hours_per_day = person("childcare_hours_per_day", period.this_year) + days_per_week = person("childcare_days_per_week", period.this_year) + base_rate = select( + [ + duration == durations.WEEKLY, + duration == durations.DAILY, + duration == durations.PART_DAY, + ], + [ + weekly_rate, + daily_rate * days_per_week, + part_day_rate * days_per_week, + ], + default=0, + ) + # A weekly rate covers the weekly maximum days; each further day is an + # additional period priced by that day's own hours. + excess_days = where( + duration == durations.WEEKLY, + max_(days_per_week - p.duration.weekly_maximum_days, 0), + 0, + ) + excess_day_rate = where( + hours_per_day >= p.duration.daily_minimum_hours, + daily_rate, + part_day_rate, + ) + # 24-OCFS-LCM-22 sec. III.4: care of 12 hours or more in a day earns + # one further period on each day, priced by the time over 12 hours, + # so a day of exactly 12 hours draws the part-day rate. + reaches_daily_maximum = hours_per_day >= p.duration.daily_maximum_hours + excess_hours = max_(hours_per_day - p.duration.daily_maximum_hours, 0) + excess_hour_rate = where( + excess_hours >= p.duration.daily_minimum_hours, + daily_rate, + part_day_rate, + ) + excess_hour_periods = where(reaches_daily_maximum, days_per_week, 0) + rate_per_week = ( + base_rate + + excess_days * excess_day_rate + + excess_hour_periods * excess_hour_rate + ) + return rate_per_week * (WEEKS_IN_YEAR / MONTHS_IN_YEAR) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py index 766beea851c..8e5fb6fac53 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py @@ -10,32 +10,22 @@ class ny_ccap(Variable): defined_for = StateCode.NY reference = ( "https://ocfs.ny.gov/programs/childcare/regulations/415-Child-Care-Services.pdf#page=39", - "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=3", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", ) def formula(spm_unit, period, parameters): person = spm_unit.members - market_rate = person("ny_ccap_market_rate", period) # ny_ccap_eligible_child carries New York's income test as well as the # categorical routes that waive it, so no separate income gate is # applied here. eligible = person("ny_ccap_eligible_child", period) in_care = person("childcare_hours_per_day", period.this_year) > 0 counted = eligible & in_care - # The unit's expenses are shared across every child in paid care, so - # an ineligible child's share stays with that child rather than - # raising an eligible sibling against its own rate ceiling. - children_in_care = spm_unit.sum(in_care) - total_expenses = spm_unit("spm_unit_pre_subsidy_childcare_expenses", period) - per_child_expenses = total_expenses / max_(children_in_care, 1) - # 18 NYCRR 415.6(e)(1) and (e)(3): payments do not exceed the actual - # cost of care, and payments per child do not exceed the applicable - # rate for the type of provider used and the age of the child. Capping - # per child rather than on pooled totals stops one child's unused rate - # headroom from absorbing a sibling's above-rate expenses. - reimbursable_care = spm_unit.sum( - min_(spm_unit.project(per_child_expenses), market_rate) * counted - ) + # Each child keeps their expense share, including an ineligible + # sibling's share. Apply differentials after the actual-cost cap so + # a qualifying payment can exceed the provider's charge. + payment = person("ny_ccap_payment_rate", period) + reimbursable_care = spm_unit.sum(payment * counted) # 415.3(e)(5): one family share per family regardless of the number of # children receiving care, so it is subtracted once from the total. family_share = spm_unit("ny_ccap_family_share", period) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap_childcare_expenses.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap_childcare_expenses.py new file mode 100644 index 00000000000..02182e5b1de --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap_childcare_expenses.py @@ -0,0 +1,23 @@ +from policyengine_us.model_api import * + + +class ny_ccap_childcare_expenses(Variable): + value_type = float + entity = Person + label = "New York CCAP monthly expenses allocated to the child" + definition_period = MONTH + unit = USD + defined_for = StateCode.NY + documentation = ( + "Allocates family expenses equally among all children in paid care, " + "including children who do not qualify for CCAP. Each child keeps " + "their own expense share when applying the market rate and " + "differentials under 18 NYCRR 415.9." + ) + reference = "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9" + + def formula(person, period, parameters): + in_care = person("childcare_hours_per_day", period.this_year) > 0 + children_in_care = person.spm_unit.sum(in_care) + total = person.spm_unit("spm_unit_pre_subsidy_childcare_expenses", period) + return total / max_(children_in_care, 1) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_accredited_provider.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_accredited_provider.py new file mode 100644 index 00000000000..841af35318f --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_accredited_provider.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class ny_ccap_accredited_provider(Variable): + value_type = bool + entity = Person + label = "New York CCAP nationally accredited provider" + definition_period = YEAR + default_value = False + defined_for = StateCode.NY + documentation = ( + "Records whether a nationally recognized child care organization has " + "accredited the child's licensed or registered provider, under 18 " + "NYCRR 415.9 and 24-OCFS-LCM-22. " + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py new file mode 100644 index 00000000000..370586ee435 --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py @@ -0,0 +1,32 @@ +from policyengine_us.model_api import * +from policyengine_us.tools.ny_ccap import ny_ccap_monthly_rate + + +class ny_ccap_day_care_center_rate(Variable): + value_type = float + entity = Person + label = "New York CCAP monthly day care center payment limit" + definition_period = MONTH + unit = USD + defined_for = StateCode.NY + documentation = ( + "Prices the child's schedule at the regular day care center rate. " + "24-OCFS-LCM-22 caps legally exempt group payments at this amount, " + "including enhancements and differentials." + ) + reference = ( + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9" + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.ny.ocfs.ccap.rates + county_group = person.household("ny_ccap_county_group", period.this_year) + age_group = person("ny_ccap_age_group", period.this_year) + return ny_ccap_monthly_rate( + person, + period, + parameters, + p.weekly[county_group]["DCC_SACC"][age_group], + p.daily[county_group]["DCC_SACC"][age_group], + p.part_day[county_group]["DCC_SACC"][age_group], + ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py new file mode 100644 index 00000000000..ad18159b949 --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py @@ -0,0 +1,60 @@ +from policyengine_us.model_api import * + + +class ny_ccap_differential(Variable): + value_type = float + entity = Person + label = "New York CCAP combined payment differential" + definition_period = MONTH + defined_for = StateCode.NY + documentation = ( + "Adds the child's nontraditional-hours, homelessness and eligible " + "provider accreditation differentials, subject to the district's " + "combined limit under 24-OCFS-LCM-22. The default limit assumes no " + "OCFS waiver. Confirmed district rates replace state defaults." + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.ny.ocfs.ccap.differentials + provider = person("childcare_provider_type_group", period.this_year) + providers = provider.possible_values + licensed = (provider == providers.DCC_SACC) | (provider == providers.FDC_GFDC) + county = person.household("county_str", period.this_year) + group = person.household("ny_ccap_county_group", period.this_year) + nyc = group == group.possible_values.GROUP_4 + monroe = county == "MONROE_COUNTY_NY" + nontraditional_rate = where( + nyc, p.nyc_nontraditional_hours, p.nontraditional_hours + ) + accreditation_rate = where(monroe, p.monroe_accreditation, p.accreditation) + exempt_homelessness_rate = where( + monroe, p.monroe_legally_exempt_homelessness, p.legally_exempt_homelessness + ) + # Optional accreditation pays zero until a district adopts it, then + # the same minimum and maximum as the mandatory differentials apply. + accreditation_rate = where( + accreditation_rate > 0, + min_(max_(accreditation_rate, p.minimum), p.maximum_individual), + 0, + ) + homelessness_rate = where( + licensed, + min_(max_(p.homelessness, p.minimum), p.maximum_individual), + min_(max_(exempt_homelessness_rate, 0), p.maximum_individual), + ) + nontraditional_rate = min_( + max_(nontraditional_rate, p.minimum), p.maximum_individual + ) + nontraditional = person("ny_ccap_nontraditional_hours", period.this_year) + homeless = person.household("is_homeless", period.this_year) + accredited = person("ny_ccap_accredited_provider", period.this_year) + combined = ( + nontraditional * nontraditional_rate + + homeless * homelessness_rate + + accredited * licensed * accreditation_rate + ) + return min_(combined, p.maximum) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_health.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_health.py new file mode 100644 index 00000000000..ac86b7cbb5c --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_health.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class ny_ccap_enhanced_health(Variable): + value_type = bool + entity = Person + label = "New York CCAP legally exempt group enhanced health requirement" + definition_period = YEAR + default_value = False + defined_for = StateCode.NY + documentation = ( + "Records whether the child's enrolled legally exempt group program " + "meets the enhanced health requirement for the district's enhanced " + "market rate, under 24-OCFS-LCM-22 and 18-OCFS-ADM-10. " + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_training.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_training.py new file mode 100644 index 00000000000..333960de1af --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_enhanced_training.py @@ -0,0 +1,19 @@ +from policyengine_us.model_api import * + + +class ny_ccap_enhanced_training(Variable): + value_type = bool + entity = Person + label = "New York CCAP legally exempt group enhanced training requirement" + definition_period = YEAR + default_value = False + defined_for = StateCode.NY + documentation = ( + "Records whether the child's enrolled legally exempt group program " + "meets the enhanced training requirement for the district's enhanced " + "market rate, under 24-OCFS-LCM-22 and 18-OCFS-ADM-10. " + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py index d79b2d8affb..466f61b9764 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py @@ -9,71 +9,25 @@ class ny_ccap_market_rate(Variable): unit = USD defined_for = StateCode.NY documentation = ( - "The applicable market rate ceiling for one child, converted to a " - "monthly amount. Under 18 NYCRR 415.9(d) care exceeding one weekly or " - "daily period earns an additional period, priced at the rate for the " - "amount of time that period covers: the daily rate at or above the " - "daily minimum hours, the part-day rate below it. A weekly schedule " - "earns one additional period per day past the weekly maximum days, " - "and any schedule earns one on each day of care at or above the daily " - "maximum hours." + "Returns the regular monthly rate, including excess care periods " + "and any legally exempt group enhancement. A child with a disability " + "receives the special-needs rate only when the parent demonstrates " + "additional care needs and the child's expense share exceeds the " + "regular rate, under 18 NYCRR 415.9 and 24-OCFS-LCM-22. Payment " + "differentials apply separately to the lesser of expenses and this rate." ) reference = ( - "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=6", - "https://ocfs.ny.gov/programs/childcare/regulations/415-Child-Care-Services.pdf#page=45", + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=7", ) def formula(person, period, parameters): - p = parameters(period).gov.states.ny.ocfs.ccap - county_group = person.household("ny_ccap_county_group", period.this_year) - provider_type = person("childcare_provider_type_group", period.this_year) - age_group = person("ny_ccap_age_group", period.this_year) - duration = person("ny_ccap_duration_of_care", period.this_year) - durations = duration.possible_values - hours_per_day = person("childcare_hours_per_day", period.this_year) - days_per_week = person("childcare_days_per_week", period.this_year) - weekly_rate = p.rates.weekly[county_group][provider_type][age_group] - daily_rate = p.rates.daily[county_group][provider_type][age_group] - part_day_rate = p.rates.part_day[county_group][provider_type][age_group] - base_rate = select( - [ - duration == durations.WEEKLY, - duration == durations.DAILY, - duration == durations.PART_DAY, - ], - [ - weekly_rate, - daily_rate * days_per_week, - part_day_rate * days_per_week, - ], - default=0, - ) - # A weekly rate covers the weekly maximum days; each further day is an - # additional period priced by that day's own hours. - excess_days = where( - duration == durations.WEEKLY, - max_(days_per_week - p.duration.weekly_maximum_days, 0), - 0, - ) - excess_day_rate = where( - hours_per_day >= p.duration.daily_minimum_hours, - daily_rate, - part_day_rate, - ) - # 24-OCFS-LCM-22 sec. III.4: care of 12 hours or more in a day earns - # one further period on each day, priced by the time over 12 hours, - # so a day of exactly 12 hours draws the part-day rate. - reaches_daily_maximum = hours_per_day >= p.duration.daily_maximum_hours - excess_hours = max_(hours_per_day - p.duration.daily_maximum_hours, 0) - excess_hour_rate = where( - excess_hours >= p.duration.daily_minimum_hours, - daily_rate, - part_day_rate, - ) - excess_hour_periods = where(reaches_daily_maximum, days_per_week, 0) - rate_per_week = ( - base_rate - + excess_days * excess_day_rate - + excess_hour_periods * excess_hour_rate - ) - return rate_per_week * (WEEKS_IN_YEAR / MONTHS_IN_YEAR) + regular = person("ny_ccap_regular_market_rate", period) + disabled = person("is_disabled", period.this_year) + additional_care = person("ny_ccap_special_needs_care", period.this_year) + expenses = person("ny_ccap_childcare_expenses", period) + qualifies = disabled & additional_care & (expenses > regular) + multiplier = parameters( + period + ).gov.states.ny.ocfs.ccap.rates.special_needs_multiplier + return regular * where(qualifies, multiplier, 1) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py new file mode 100644 index 00000000000..24d9c71823a --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py @@ -0,0 +1,20 @@ +from policyengine_us.model_api import * + + +class ny_ccap_nontraditional_hours(Variable): + value_type = bool + entity = Person + label = "New York CCAP care during nontraditional hours" + definition_period = YEAR + default_value = False + defined_for = StateCode.NY + documentation = ( + "Records whether the child receives care before 6 a.m. or after 7 " + "p.m. Monday through Friday, at any time on Saturday or Sunday, or on " + "New Year's Day, Memorial Day, Independence Day, Labor Day, " + "Thanksgiving Day, or Christmas Day, under 24-OCFS-LCM-22. " + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py new file mode 100644 index 00000000000..57e8c31a7b4 --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py @@ -0,0 +1,30 @@ +from policyengine_us.model_api import * + + +class ny_ccap_payment_rate(Variable): + value_type = float + entity = Person + label = "New York CCAP monthly payment before family share" + definition_period = MONTH + unit = USD + defined_for = StateCode.NY + documentation = ( + "Applies the combined differential to the lesser of the child's " + "expenses and applicable market rate. Under 24-OCFS-LCM-22, the " + "result can exceed the provider's charge. Legally exempt group " + "payments cannot exceed the corresponding day care center rate." + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + ) + + def formula(person, period, parameters): + expenses = person("ny_ccap_childcare_expenses", period) + market_rate = person("ny_ccap_market_rate", period) + differential = person("ny_ccap_differential", period) + payment = min_(expenses, market_rate) * (1 + differential) + provider = person("childcare_provider_type_group", period.this_year) + group_provider = provider == provider.possible_values.LE_GC + center_rate = person("ny_ccap_day_care_center_rate", period) + return where(group_provider, min_(payment, center_rate), payment) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py new file mode 100644 index 00000000000..dd0ec92066b --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py @@ -0,0 +1,53 @@ +from policyengine_us.model_api import * +from policyengine_us.tools.ny_ccap import ny_ccap_monthly_rate + + +class ny_ccap_regular_market_rate(Variable): + value_type = float + entity = Person + label = "New York CCAP regular monthly market rate" + definition_period = MONTH + unit = USD + defined_for = StateCode.NY + documentation = ( + "Prices scheduled and excess care periods before the special-needs " + "substitution or payment differentials. New York City pays enhanced " + "legally exempt group rates when the program meets its health or " + "training requirements. Other districts retain standard group rates " + "where no district enhancement has been confirmed." + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + "https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2", + ) + + def formula(person, period, parameters): + p = parameters(period).gov.states.ny.ocfs.ccap.rates + county_group = person.household("ny_ccap_county_group", period.this_year) + provider_type = person("childcare_provider_type_group", period.this_year) + age_group = person("ny_ccap_age_group", period.this_year) + providers = provider_type.possible_values + health = person("ny_ccap_enhanced_health", period.this_year) + training = person("ny_ccap_enhanced_training", period.this_year) + enhanced_fraction = select( + [health & training, health | training], + [ + p.enhanced_group.both[county_group], + p.enhanced_group.single[county_group], + ], + default=0, + ) + enhanced = (provider_type == providers.LE_GC) & (enhanced_fraction > 0) + # OCFS rounds each enhanced period rate to the nearest whole dollar, + # with a half dollar rounded up. Under-three group rates stay zero. + has_group_rate = p.weekly[county_group]["LE_GC"][age_group] > 0 + period_rates = [] + for table in (p.weekly, p.daily, p.part_day): + standard = table[county_group][provider_type][age_group] + center = table[county_group]["DCC_SACC"][age_group] + enhanced_rate = np.floor(center * enhanced_fraction + 0.5) + period_rates.append( + where(enhanced & has_group_rate, enhanced_rate, standard) + ) + return ny_ccap_monthly_rate(person, period, parameters, *period_rates) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_care.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_care.py new file mode 100644 index 00000000000..ea579f9a48c --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_care.py @@ -0,0 +1,21 @@ +from policyengine_us.model_api import * + + +class ny_ccap_special_needs_care(Variable): + value_type = bool + entity = Person + label = "New York CCAP parent demonstrates additional special needs care" + definition_period = YEAR + default_value = False + defined_for = StateCode.NY + documentation = ( + "Records whether the parent or caretaker has demonstrated to the " + "district that the child needs child care services beyond those " + "children of similar age generally require, under 18 NYCRR 415.9 and " + "24-OCFS-LCM-22. The rate also requires disability and expenses " + "exceeding the regular market rate. " + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=7", + ) From 80bf30b67f66ace2c602c47bb903afac3be581f7 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 12 Nov 2026 00:11:12 -0500 Subject: [PATCH 2/7] Check New York CCAP rates against calculated boundaries Co-Authored-By: Claude Opus 5.5 --- policyengine_us/programs.yaml | 2 +- .../core/test_ny_ccap_rates_invariants.py | 152 ++++++++++++++---- .../gov/states/ny/ocfs/ccap/integration.yaml | 10 +- 3 files changed, 131 insertions(+), 33 deletions(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 4566283129f..81361b0a63c 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -875,7 +875,7 @@ programs: full_name: New York Child Care Assistance Program variable: ny_ccap parameter_prefix: gov.states.ny.ocfs.ccap - notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share, including the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. District percentages use the October 2024 state minimums or optional zeros where no local rate has been confirmed; later differential amendments remain outside this implementation. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share + notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share, including the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. The model uses October 2024 state defaults where it does not encode district-specific differential or enhanced rates, and does not include later differential amendments. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share - state: OR status: complete name: Oregon ERDC diff --git a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py index 2d9bff6a2e2..d99f03d4c55 100644 --- a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py +++ b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py @@ -37,6 +37,7 @@ (8, 7, (1, 2, 0)), (12, 5, (1, 0, 5)), (18, 7, (1, 9, 0)), + (8, 5, (1, 0, 0)), ) LEGACY_NYC_ENHANCED = { "weekly": (280, 245, 228, 202), @@ -57,26 +58,30 @@ "at_regular", "below_regular", "special_differentials", + "all", ) FLAGS = { "ny_ccap_nontraditional_hours": { "nontraditional", "differentials", "special_differentials", + "all", }, "ny_ccap_accredited_provider": { "accredited", "differentials", "special_differentials", + "all", }, - "ny_ccap_enhanced_health": {"health", "enhancements"}, - "ny_ccap_enhanced_training": {"training", "enhancements"}, + "ny_ccap_enhanced_health": {"health", "enhancements", "all"}, + "ny_ccap_enhanced_training": {"training", "enhancements", "all"}, "ny_ccap_special_needs_care": { "special", "unqualified", "at_regular", "below_regular", "special_differentials", + "all", }, } @@ -122,6 +127,21 @@ def _coverage_households(): } for group_index, group in enumerate(GROUPS) for provider_index, provider in enumerate(PROVIDERS) + ] + [ + { + "group": "GROUP_4", + "children": [ + { + "provider": "LE_GC", + "age": "PRESCHOOLER", + "schedule": SCHEDULES.index((8, 5, (1, 0, 0))), + "eligible": True, + "disabled": False, + } + ], + "expenses": 10_000, + "family_share": 0, + } ] @@ -177,6 +197,7 @@ def _simulate(households, parameters): "at_regular", "below_regular", "special_differentials", + "all", }: disabled = True elif variant == "unqualified": @@ -192,20 +213,16 @@ def _simulate(households, parameters): "ny_ccap_eligible_child": {MONTH: case["eligible"]}, "ny_ccap_childcare_expenses": {MONTH: expense}, } - if variant == "at_regular": - # Store both sides through the same float32 input path. - person["ny_ccap_regular_market_rate"] = {MONTH: regular} # Omit false inputs so the default twins check their defaults. for flag, variants in FLAGS.items(): if variant in variants: person[flag] = {YEAR: True} - person_index = len(situation["people"]) situation["people"][name] = person situation["marital_units"][name] = {"members": [name]} child_rows.append( { **case, - "person_index": person_index, + "person_id": name, "expense": expense, "regular": regular, "legacy": _table_rate( @@ -239,11 +256,41 @@ def _simulate(households, parameters): "ny_ccap_county_group": {YEAR: unit["group"]}, "is_homeless": { YEAR: variant - in {"homeless", "differentials", "special_differentials"} + in {"homeless", "differentials", "special_differentials", "all"} }, } - rows.append({**unit, "variant": variant, "children": child_rows}) - return Simulation(situation=situation, tax_benefit_system=system), rows + rows.append( + { + **unit, + "spm_id": unit_name, + "variant": variant, + "children": child_rows, + } + ) + simulation = Simulation(situation=situation, tax_benefit_system=system) + person_indices = { + str(person_id): index for index, person_id in enumerate(simulation.persons.ids) + } + spm_indices = { + str(spm_id): index + for index, spm_id in enumerate(simulation.populations["spm_unit"].ids) + } + for row in rows: + row["spm_index"] = spm_indices[row["spm_id"]] + for case in row["children"]: + case["person_index"] = person_indices[case["person_id"]] + # Match the actual float32 ceiling at the expense threshold. Supplying a + # partial regular-rate input would replace every omitted person's formula + # with the variable's zero default, so leave regular rates as calculations. + regular = simulation.calculate("ny_ccap_regular_market_rate", MONTH) + expenses = simulation.calculate("ny_ccap_childcare_expenses", MONTH).copy() + for row in rows: + if row["variant"] == "at_regular": + for case in row["children"]: + index = case["person_index"] + case["expense"] = expenses[index] = regular[index] + simulation.set_input("ny_ccap_childcare_expenses", MONTH, expenses) + return simulation, rows @settings( @@ -274,14 +321,20 @@ def calc(variable, period=MONTH): for case in row["children"] ] for flag in FLAGS: - assert not calc(flag, YEAR)[default_indices].any() - assert np.isfinite(benefit).all() and (benefit >= 0).all() - assert (payment >= 0).all() - assert ((differential >= 0) & (differential <= 0.25 + 1e-7)).all() + assert not calc(flag, YEAR)[default_indices].any(), f"default flag: {flag}" + assert np.isfinite(benefit).all() and (benefit >= 0).all(), "nonnegative benefits" + assert (payment >= 0).all(), "nonnegative payment rates" + assert ((differential >= 0) & (differential <= 0.25 + 1e-7)).all(), ( + "differential bounds" + ) # Every payment stays within the ordinary 25% differential ceiling. - assert (payment <= np.minimum(expenses, market) * 1.25 + 0.01).all() + assert (payment <= np.minimum(expenses, market) * 1.25 + 0.01).all(), ( + "payment bounds" + ) - for row_index, row in enumerate(rows): + for row in rows: + row_index = row["spm_index"] + context = f"{row['spm_id']}: group={row['group']}, children={row['children']}" indices = [case["person_index"] for case in row["children"]] counted = np.array( [ @@ -292,24 +345,59 @@ def calc(variable, period=MONTH): np.testing.assert_allclose( benefit[row_index], max(payment[indices][counted].sum() - row["family_share"], 0), + err_msg=f"payment aggregation: {context}", **TOLERANCE, ) for case in row["children"]: i = case["person_index"] if case["provider"] == "LE_GC": - assert payment[i] <= center[i] + 0.01 + assert payment[i] <= center[i] + 0.01, f"group payment cap: {context}" + if ( + row["variant"] == "all" + and row["group"] == "GROUP_4" + and regular[i] > 0 + and expenses[i] >= center[i] + ): + np.testing.assert_allclose( + payment[i], + center[i], + err_msg=f"NYC group cap binds: {context}", + **TOLERANCE, + ) if row["variant"] == "default": if row["group"] == "GROUP_4" and case["provider"] == "LE_ENH": # ACS's 75% informal rate intentionally exceeds main's 70%. - assert market[i] >= case["legacy"] - 0.01 + assert market[i] >= case["legacy"] - 0.01, ( + f"NYC enhancement: {context}" + ) else: - np.testing.assert_allclose(market[i], case["legacy"], **TOLERANCE) - np.testing.assert_allclose(regular[i], case["regular"], **TOLERANCE) - if row["variant"] in {"special", "special_differentials"}: + np.testing.assert_allclose( + market[i], + case["legacy"], + err_msg=f"default market: {context}", + **TOLERANCE, + ) + np.testing.assert_allclose( + regular[i], + case["regular"], + err_msg=f"regular rate: {context}", + **TOLERANCE, + ) + if row["variant"] in {"special", "special_differentials", "all"}: expected = regular[i] * (1.15 if expenses[i] > regular[i] else 1) - np.testing.assert_allclose(market[i], expected, **TOLERANCE) + np.testing.assert_allclose( + market[i], + expected, + err_msg=f"special-needs substitution: {context}", + **TOLERANCE, + ) elif row["variant"] in {"unqualified", "at_regular", "below_regular"}: - np.testing.assert_allclose(market[i], regular[i], **TOLERANCE) + np.testing.assert_allclose( + market[i], + regular[i], + err_msg=f"special-needs exclusion: {context}", + **TOLERANCE, + ) if row["variant"] == "default": legacy_payment = sum( min(case["expense"], case["legacy"]) @@ -321,10 +409,15 @@ def calc(variable, period=MONTH): case["provider"] == "LE_ENH" for case in row["children"] ) if has_nyc_enhanced: - assert benefit[row_index] >= legacy_benefit - 0.01 + assert benefit[row_index] >= legacy_benefit - 0.01, ( + f"NYC benefit: {context}" + ) else: np.testing.assert_allclose( - benefit[row_index], legacy_benefit, **TOLERANCE + benefit[row_index], + legacy_benefit, + err_msg=f"default benefit: {context}", + **TOLERANCE, ) # Each twin uses the same expenses, care and family share as its default. @@ -339,8 +432,13 @@ def calc(variable, period=MONTH): "health", "training", "enhancements", + "all", ): position = start + VARIANTS.index(variant) indices = [case["person_index"] for case in rows[position]["children"]] - assert (payment[indices] >= payment[base_indices] - 0.01).all() - assert benefit[position] >= benefit[start] - 0.01 + context = f"{base['spm_id']} -> {rows[position]['spm_id']}" + assert (payment[indices] >= payment[base_indices] - 0.01).all(), context + assert ( + benefit[rows[position]["spm_index"]] + >= benefit[base["spm_index"]] - 0.01 + ), context diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/integration.yaml index 7667e070c45..f5641ea168b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/integration.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/integration.yaml @@ -95,12 +95,12 @@ ny_ccap_county_group: GROUP_4 ny_ccap_age_group: SCHOOL_AGE ny_ccap_duration_of_care: PART_DAY - # Market rate: $28/part-day * 3 days/week * 52 / 12. - ny_ccap_market_rate: 28 * 3 * 52 / 12 + # ACS FS-023: $30/part-day * 3 days/week * 52 / 12 = $390. + ny_ccap_market_rate: 30 * 3 * 52 / 12 ny_ccap_family_share: 52 / 12 - ny_ccap: (28 * 3 * 52 - 52) / 12 - ny_child_care_subsidies: (28 * 3 * 52 - 52) / 12 - child_care_subsidies: (28 * 3 * 52 - 52) / 12 + ny_ccap: (30 * 3 * 52 - 52) / 12 + ny_child_care_subsidies: (30 * 3 * 52 - 52) / 12 + child_care_subsidies: (30 * 3 * 52 - 52) / 12 absolute_error_margin: 0.01 # The activity test is the sole cause of ineligibility here: hhs_smi is set From 2460e588f620900c6e2735e4d8640495139fbad8 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 12 Nov 2026 00:17:39 -0500 Subject: [PATCH 3/7] Preserve New York CCAP source references Co-Authored-By: Claude Opus 5.5 --- policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py | 3 ++- .../states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py index 8e5fb6fac53..28bfd48e0d4 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/ny_ccap.py @@ -10,7 +10,8 @@ class ny_ccap(Variable): defined_for = StateCode.NY reference = ( "https://ocfs.ny.gov/programs/childcare/regulations/415-Child-Care-Services.pdf#page=39", - "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + # PDF pages 3, 9-10 + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=3", ) def formula(spm_unit, period, parameters): diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py index dd0ec92066b..bda3763923f 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_regular_market_rate.py @@ -18,7 +18,9 @@ class ny_ccap_regular_market_rate(Variable): ) reference = ( "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", - "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + # PDF pages 6, 8-9 + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=6", + "https://ocfs.ny.gov/programs/childcare/regulations/415-Child-Care-Services.pdf#page=45", "https://www.nyc.gov/assets/acs/pdf/earlycare/2024/FS023.pdf#page=2", ) From dd4d48b8f0d1a0a579ec1b10f71cf06cb830ee35 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 12 Nov 2026 00:26:40 -0500 Subject: [PATCH 4/7] Clarify New York CCAP nontraditional care hours Co-Authored-By: Claude Opus 5.5 --- .../ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py index 24d9c71823a..a75b877f7b3 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py @@ -9,10 +9,11 @@ class ny_ccap_nontraditional_hours(Variable): default_value = False defined_for = StateCode.NY documentation = ( - "Records whether the child receives care before 6 a.m. or after 7 " - "p.m. Monday through Friday, at any time on Saturday or Sunday, or on " + "Records whether the child receives care before 6 a.m. or after " + "7 p.m. Monday through Friday. Any care on Saturday, Sunday, " "New Year's Day, Memorial Day, Independence Day, Labor Day, " - "Thanksgiving Day, or Christmas Day, under 24-OCFS-LCM-22. " + "Thanksgiving Day, or Christmas Day also qualifies under " + "24-OCFS-LCM-22." ) reference = ( "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", From 2d30c5dd267c030783ba6be7bd064a83d31dc7b2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 12 Nov 2026 00:35:45 -0500 Subject: [PATCH 5/7] Describe New York CCAP rate approximations Co-Authored-By: Claude Opus 5.5 --- policyengine_us/programs.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 81361b0a63c..65ab6a0a7e1 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -875,7 +875,7 @@ programs: full_name: New York Child Care Assistance Program variable: ny_ccap parameter_prefix: gov.states.ny.ocfs.ccap - notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share, including the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. The model uses October 2024 state defaults where it does not encode district-specific differential or enhanced rates, and does not include later differential amendments. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share + notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share. Includes the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. The model approximates district choices with October 2024 state defaults outside its local rate overrides. Later differential amendments remain outside this implementation. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share - state: OR status: complete name: Oregon ERDC From 24ee73c6d45d78e20b68e549936e3c6ad80d97ff Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Fri, 13 Nov 2026 17:48:00 -0500 Subject: [PATCH 6/7] Apply New York CCAP's 2025 differential minimum and combined limit Social Services Law 410-x(10) requires at least 10% for nontraditional hours and for licensed or registered care of homeless children from April 1, 2025. An elected accreditation differential keeps its own 5% minimum. OCFS raised the combined limit to 35% in a rule effective December 24, 2025. LCM-22 makes the special-needs rate the market rate for a qualifying child, so the day care center rate that caps legally exempt group payments now rises with it. The payment tests share two sets of parameter overrides instead of six, and new cases cover the licensed homelessness floor, the April 2025 boundary, Monroe County in November 2024 and the 2026 limit. Co-Authored-By: Claude Opus 5.5 --- .../ny-ccap-rate-differentials.added.md | 2 +- .../ccap/differentials/accreditation.yaml | 7 +- .../differentials/accreditation_minimum.yaml | 11 + .../ocfs/ccap/differentials/homelessness.yaml | 10 +- .../legally_exempt_homelessness.yaml | 7 +- .../ny/ocfs/ccap/differentials/maximum.yaml | 10 +- .../differentials/maximum_individual.yaml | 3 +- .../ny/ocfs/ccap/differentials/minimum.yaml | 8 +- .../differentials/nontraditional_hours.yaml | 8 +- policyengine_us/programs.yaml | 2 +- .../core/test_ny_ccap_rates_invariants.py | 40 +- .../ocfs/ccap/rates/ny_ccap_payment_rate.yaml | 574 +++++++++++++----- .../rates/ny_ccap_day_care_center_rate.py | 18 +- .../ocfs/ccap/rates/ny_ccap_differential.py | 18 +- .../ny/ocfs/ccap/rates/ny_ccap_market_rate.py | 5 +- .../rates/ny_ccap_nontraditional_hours.py | 6 +- .../ocfs/ccap/rates/ny_ccap_payment_rate.py | 3 +- .../ny_ccap_special_needs_rate_eligible.py | 26 + 18 files changed, 569 insertions(+), 189 deletions(-) create mode 100644 policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation_minimum.yaml create mode 100644 policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_rate_eligible.py diff --git a/changelog.d/ny-ccap-rate-differentials.added.md b/changelog.d/ny-ccap-rate-differentials.added.md index 29a53295f1d..5c33fcfb48b 100644 --- a/changelog.d/ny-ccap-rate-differentials.added.md +++ b/changelog.d/ny-ccap-rate-differentials.added.md @@ -1 +1 @@ -Added New York CCAP payment differentials, the special-needs market rate, and New York City enhanced informal and legally exempt group rates under the October 2024 schedule. +Added New York CCAP payment differentials, including the 10% statutory minimum from April 2025 and the 35% combined limit from December 2025, the special-needs market rate, and New York City enhanced informal and legally exempt group rates. diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml index 075d338fc22..34ec43bc6a3 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation.yaml @@ -1,7 +1,6 @@ description: New York districts pay this default optional differential to nationally accredited licensed or registered providers. -# Use the October 2024 state minimum or optional zero where no district -# percentage has been confirmed. Separate parameters hold confirmed local rates. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +# Pay nothing until a district elects this optional differential. Separate +# parameters hold confirmed local rates. values: 2024-10-01: 0 metadata: @@ -9,6 +8,6 @@ metadata: label: New York CCAP accreditation differential reference: - title: 24-OCFS-LCM-22, differential payment rates - href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=10 - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation_minimum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation_minimum.yaml new file mode 100644 index 00000000000..947133f7e9a --- /dev/null +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/accreditation_minimum.yaml @@ -0,0 +1,11 @@ +description: New York requires at least this accreditation differential when a district elects to pay one. +values: + 2024-10-01: 0.05 +metadata: + unit: /1 + label: New York CCAP accreditation minimum differential + reference: + - title: 24-OCFS-LCM-22, differential payment rates + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=10 + - title: 18 NYCRR 415.9 + href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml index 4b5c769efe4..a0818322b65 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/homelessness.yaml @@ -1,14 +1,16 @@ description: New York districts pay this default differential to licensed or registered providers caring for children experiencing homelessness. -# Use the October 2024 state minimum or optional zero where no district -# percentage has been confirmed. Separate parameters hold confirmed local rates. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +# Districts without a confirmed local percentage pay the state minimum. +# Separate parameters hold confirmed local rates. values: 2024-10-01: 0.05 + 2025-04-01: 0.1 metadata: unit: /1 label: New York CCAP homelessness differential reference: - title: 24-OCFS-LCM-22, differential payment rates - href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=10 + - title: NY Social Services Law 410-x(10)(a), effective April 1, 2025 + href: https://www.nysenate.gov/legislation/laws/SOS/410-X - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml index 4a94637baff..8351f641a8c 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/legally_exempt_homelessness.yaml @@ -1,7 +1,6 @@ description: New York districts pay this default optional differential to legally exempt providers caring for children experiencing homelessness. -# Use the October 2024 state minimum or optional zero where no district -# percentage has been confirmed. Separate parameters hold confirmed local rates. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +# Pay nothing until a district elects this optional differential. Separate +# parameters hold confirmed local rates. values: 2024-10-01: 0 metadata: @@ -9,6 +8,6 @@ metadata: label: New York CCAP legally exempt homelessness differential reference: - title: 24-OCFS-LCM-22, differential payment rates - href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=10 - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml index c954dc518cd..505d5709686 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum.yaml @@ -1,12 +1,18 @@ description: New York limits combined differentials to this fraction of the lesser of actual cost and the applicable market rate unless OCFS grants a waiver. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +# OCFS raised the limit from 25% to 35% in a rule effective December 24, +# 2025. The monthly model first reads the higher limit in January 2026. values: 2024-10-01: 0.25 + 2025-12-24: 0.35 metadata: unit: /1 label: New York CCAP maximum differential reference: - title: 24-OCFS-LCM-22, differential payment rates - href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=10 + - title: NYS Register, March 12, 2025, I.D. No. CFS-10-25-00002-P + href: https://dos.ny.gov/system/files/documents/2025/03/031225.pdf#page=7 + - title: NYS Register, December 24, 2025, I.D. No. CFS-10-25-00002-A + href: https://dos.ny.gov/system/files/documents/2025/12/december-24-2025-vol.xlvii-issue-51.pdf#page=7 - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml index 59b226206b8..703f0764877 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml @@ -1,5 +1,4 @@ description: New York limits each differential to this fraction of the lesser of actual cost and the applicable market rate. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. values: 2024-10-01: 0.15 metadata: @@ -8,5 +7,7 @@ metadata: reference: - title: 24-OCFS-LCM-22, differential payment rates href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: NY Social Services Law 410-x(10), effective April 1, 2025 + href: https://www.nysenate.gov/legislation/laws/SOS/410-X - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml index d0a40c8d3d0..5c743aff0d0 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml @@ -1,12 +1,14 @@ -description: New York requires at least this differential for nontraditional hours and licensed or registered homelessness care, and for accreditation when a district elects it. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +description: New York requires at least this differential for care during nontraditional hours and for licensed or registered providers caring for children experiencing homelessness. values: 2024-10-01: 0.05 + 2025-04-01: 0.1 metadata: unit: /1 - label: New York CCAP minimum differential + label: New York CCAP mandatory minimum differential reference: - title: 24-OCFS-LCM-22, differential payment rates href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: NY Social Services Law 410-x(10), effective April 1, 2025 + href: https://www.nysenate.gov/legislation/laws/SOS/410-X - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml index cf2637c795f..49448df4b40 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/nontraditional_hours.yaml @@ -1,14 +1,16 @@ description: New York districts pay this default differential for care during nontraditional hours. -# Use the October 2024 state minimum or optional zero where no district -# percentage has been confirmed. Separate parameters hold confirmed local rates. -# This implementation follows 24-OCFS-LCM-22; later amendments need an update. +# Districts without a confirmed local percentage pay the state minimum. +# Separate parameters hold confirmed local rates. values: 2024-10-01: 0.05 + 2025-04-01: 0.1 metadata: unit: /1 label: New York CCAP nontraditional hours differential reference: - title: 24-OCFS-LCM-22, differential payment rates href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 + - title: NY Social Services Law 410-x(10)(b), effective April 1, 2025 + href: https://www.nysenate.gov/legislation/laws/SOS/410-X - title: 18 NYCRR 415.9 href: https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9 diff --git a/policyengine_us/programs.yaml b/policyengine_us/programs.yaml index 65ab6a0a7e1..1c89b930bf9 100644 --- a/policyengine_us/programs.yaml +++ b/policyengine_us/programs.yaml @@ -875,7 +875,7 @@ programs: full_name: New York Child Care Assistance Program variable: ny_ccap parameter_prefix: gov.states.ny.ocfs.ccap - notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share. Includes the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. The model approximates district choices with October 2024 state defaults outside its local rate overrides. Later differential amendments remain outside this implementation. Not modeled - safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share + notes: Models New York income eligibility, market rates, payment differentials, the special-needs rate, New York City enhanced informal and group rates, and the family share. Includes the foster-care and child-protective-services categorical routes, the 18 NYCRR 415.1(b) age extensions for special needs and court supervision, and the statutory family-share exceptions. The model prices excess periods for a sixth or seventh day of care, and for each day of care at or above the daily maximum hours, at the applicable daily or part-day rate under 24-OCFS-LCM-22. Without a county or county FIPS input, the model places a New York household in Albany County, in county group 2. That placement overstates rates for households in the 38 group 3 counties and understates them in New York City and the five group 1 counties. An UNKNOWN county falls to group 3, the lowest-rate group. The benefit requires childcare_hours_per_day and childcare_days_per_week as well as expenses, because OCFS sets market rates by duration of care; expenses alone yield no benefit. OCFS dropped hourly market rates on June 1, 2022 (22-OCFS-LCM-14); 18 NYCRR 415.9 and 24-OCFS-LCM-22 set weekly, daily and part-day rates only. Outside its New York City and Monroe County rates, the model uses state defaults for district choices, with the minimum for mandatory differentials (5%, then 10% from April 1, 2025, under Social Services Law 410-x(10)) and zero for optional ones. The combined limit rises from 25% to 35% on December 24, 2025. Not modeled - the optional differentials for QUALITYstarsNY and the auto-injector initiative that OCFS added on December 24, 2025, Monroe County's 75% enhanced informal rate, safety net assistance as public assistance for the family-share exception, preventive services as distinct from protective services, public assistance in countable income (omitted to avoid a circular dependency through the TANF dependent care deduction), the required-work-activity condition on the 415.2(a)(1) guarantee of care to public-assistance families (public assistance enrollment alone satisfies the reason-for-care test), and the child-only assistance unit exception to the family share - state: OR status: complete name: Oregon ERDC diff --git a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py index d99f03d4c55..fe32eb077ab 100644 --- a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py +++ b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py @@ -225,6 +225,15 @@ def _simulate(households, parameters): "person_id": name, "expense": expense, "regular": regular, + "special_needs_inputs": disabled + and variant in FLAGS["ny_ccap_special_needs_care"], + "center_table": _table_rate( + parameters, + unit["group"], + "DCC_SACC", + case["age"], + case["schedule"], + ), "legacy": _table_rate( parameters, unit["group"], @@ -312,6 +321,7 @@ def calc(variable, period=MONTH): expenses = calc("ny_ccap_childcare_expenses") payment = calc("ny_ccap_payment_rate") center = calc("ny_ccap_day_care_center_rate") + special_needs = calc("ny_ccap_special_needs_rate_eligible").astype(bool) differential = calc("ny_ccap_differential") benefit = calc("ny_ccap") default_indices = [ @@ -350,20 +360,26 @@ def calc(variable, period=MONTH): ) for case in row["children"]: i = case["person_index"] + # The special-needs rate needs disability, the parent's showing + # and expenses above the regular rate. + assert special_needs[i] == ( + case["special_needs_inputs"] and expenses[i] > regular[i] + ), f"special-needs eligibility: {context}" if case["provider"] == "LE_GC": + # A qualifying child's center rate rises to the special-needs rate. + np.testing.assert_allclose( + center[i], + case["center_table"] * (1.15 if special_needs[i] else 1), + err_msg=f"group payment cap rate: {context}", + **TOLERANCE, + ) assert payment[i] <= center[i] + 0.01, f"group payment cap: {context}" - if ( - row["variant"] == "all" - and row["group"] == "GROUP_4" - and regular[i] > 0 - and expenses[i] >= center[i] - ): - np.testing.assert_allclose( - payment[i], - center[i], - err_msg=f"NYC group cap binds: {context}", - **TOLERANCE, - ) + np.testing.assert_allclose( + payment[i], + min(min(expenses[i], market[i]) * (1 + differential[i]), center[i]), + err_msg=f"group payment: {context}", + **TOLERANCE, + ) if row["variant"] == "default": if row["group"] == "GROUP_4" and case["provider"] == "LE_ENH": # ACS's 75% informal rate intentionally exceeds main's 70%. diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml index 842eb1e2d33..9dbe6ac997c 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml @@ -1,3 +1,11 @@ +# Each set of parameter overrides builds a separate tax-benefit system, so +# the cases below share two sets, each listed in the same order every time: +# - a district that configures nontraditional and homelessness rates of 0, +# a 2% exempt homelessness rate, a 1% accreditation rate and an OCFS +# waiver to 40%; +# - a district that configures 30% for every differential. +# Every other case uses the default parameters. + # 24-OCFS-LCM-22 applies each differential to min(expenses, market rate). # $800 * 1.05 = $840, so the district can pay more than the $800 charge. - name: Nontraditional hours raise payment above a charge below the market rate. @@ -60,67 +68,6 @@ ny_ccap_payment_rate: 1_000 absolute_error_margin: 0.01 -# A district that elects a 5% accreditation differential pays $1,000 * 1.05. -- name: Accreditation raises payment when the district elects the differential. - period: 2025-01 - input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 - state_code: NY - ny_ccap_county_group: GROUP_1 - childcare_provider_type_group: DCC_SACC - ny_ccap_childcare_expenses: 1_200 - ny_ccap_market_rate: 1_000 - ny_ccap_accredited_provider: true - output: - ny_ccap_differential: 0.05 - ny_ccap_payment_rate: 1_050 - absolute_error_margin: 0.01 - -# The district adds three 5% differentials: $1,000 * (1 + .05 * 3) = $1,150. -- name: A licensed provider receives all three differentials together. - period: 2025-01 - input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 - state_code: NY - ny_ccap_county_group: GROUP_1 - childcare_provider_type_group: DCC_SACC - ny_ccap_childcare_expenses: 1_200 - ny_ccap_market_rate: 1_000 - ny_ccap_nontraditional_hours: true - ny_ccap_accredited_provider: true - is_homeless: true - output: - ny_ccap_differential: 0.15 - ny_ccap_payment_rate: 1_150 - absolute_error_margin: 0.01 - -# min(.15 + .15 + .15, .25) = .25; $1,000 * 1.25 = $1,250. -- name: The total differential stops at twenty-five percent without a waiver. - period: 2025-01 - input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 - state_code: NY - ny_ccap_county_group: GROUP_1 - childcare_provider_type_group: FDC_GFDC - ny_ccap_childcare_expenses: 2_000 - ny_ccap_market_rate: 1_000 - ny_ccap_nontraditional_hours: true - ny_ccap_accredited_provider: true - is_homeless: true - output: - ny_ccap_differential: 0.25 - ny_ccap_payment_rate: 1_250 - absolute_error_margin: 0.01 - # The state default pays no optional homelessness differential to exempt care. - name: Homelessness leaves standard exempt informal care unchanged by default. period: 2025-01 @@ -152,70 +99,85 @@ ny_ccap_payment_rate: 1_050 absolute_error_margin: 0.01 -# The district may elect an exempt-provider homelessness rate below 5%. -# $1,000 * 1.02 = $1,020. -- name: Exempt care receives an optional homelessness rate without a minimum. - period: 2025-01 +# Social Services Law 410-x(10) requires at least 10% from April 1, 2025. +# March keeps the 5% minimum: $1,000 * 1.05 = $1,050. +- name: March 2025 keeps the five-percent nontraditional-hours minimum. + period: 2025-03 input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.05 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.05 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.05 - state_code: NY - ny_ccap_county_group: GROUP_1 - childcare_provider_type_group: LE_STD + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + childcare_provider_type_group: {2025: DCC_SACC} ny_ccap_childcare_expenses: 1_200 ny_ccap_market_rate: 1_000 - is_homeless: true + ny_ccap_nontraditional_hours: {2025: true} output: - ny_ccap_differential: 0.02 - ny_ccap_payment_rate: 1_020 + ny_ccap_differential: 0.05 + ny_ccap_payment_rate: 1_050 absolute_error_margin: 0.01 -# Exempt providers never receive the accreditation differential. -- name: Accreditation cannot raise payment for exempt informal care. - period: 2025-01 +# April pays the 10% statutory minimum: $1,000 * 1.10 = $1,100. +- name: April 2025 raises the nontraditional-hours minimum to ten percent. + period: 2025-04 input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 - state_code: NY - ny_ccap_county_group: GROUP_1 - childcare_provider_type_group: LE_STD + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + childcare_provider_type_group: {2025: DCC_SACC} ny_ccap_childcare_expenses: 1_200 ny_ccap_market_rate: 1_000 - ny_ccap_accredited_provider: true + ny_ccap_nontraditional_hours: {2025: true} output: - ny_ccap_differential: 0 - ny_ccap_payment_rate: 1_000 + ny_ccap_differential: 0.10 + ny_ccap_payment_rate: 1_100 absolute_error_margin: 0.01 -# NYC's enhanced group rate pays $382 weekly for both requirements. -# min($3,000, $382 * 52 / 12) * 1.25 exceeds $439 * 52 / 12, -# so the district pays the center rate, $1,902.33 monthly. -- name: Exempt group payment stops at the center rate after differentials. - period: 2025-01 +# In June 2025, 410-x(10)(b) gives an exempt provider at least 10% for +# nontraditional hours, and 410-x(10)(a) gives licensed or registered +# providers at least 10% for homelessness: $1,000 * 1.10 = $1,100. +# Exempt homelessness care stays optional, at 0% by default. +- name: From April 2025 the mandatory differentials pay at least ten percent. + period: 2025-06 input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 - state_code: NY - ny_ccap_county_group: GROUP_4 - childcare_provider_type_group: LE_GC - ny_ccap_age_group: PRESCHOOLER - ny_ccap_duration_of_care: WEEKLY - ny_ccap_childcare_expenses: 3_000 - ny_ccap_nontraditional_hours: true - ny_ccap_enhanced_health: true - ny_ccap_enhanced_training: true - is_homeless: true + people: + exempt_nontraditional: + childcare_provider_type_group: {2025: LE_STD} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + center_homeless: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + family_homeless: + childcare_provider_type_group: {2025: FDC_GFDC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + exempt_homeless: + childcare_provider_type_group: {2025: LE_STD} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + households: + exempt_nontraditional: + members: [exempt_nontraditional] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + center_homeless: + members: [center_homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + family_homeless: + members: [family_homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + exempt_homeless: + members: [exempt_homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} output: - ny_ccap_market_rate: 382 * 52 / 12 - ny_ccap_differential: 0.25 - ny_ccap_day_care_center_rate: 439 * 52 / 12 - ny_ccap_payment_rate: 439 * 52 / 12 + ny_ccap_differential: [0.10, 0.10, 0.10, 0] + ny_ccap_payment_rate: [1_100, 1_100, 1_100, 1_000] absolute_error_margin: 0.01 # For a qualifying child, $1,000 * 1.15 = $1,150 becomes the market rate. @@ -255,6 +217,35 @@ ny_ccap_payment_rate: 1_155 absolute_error_margin: 0.01 +# NYC pays $382 weekly for a group program meeting both enhancements and +# $439 for a center. LCM-22 makes the special-needs rate the market rate for a +# qualifying child, so both rise by 15%: $382 * 1.15 = $439.30 and $439 * 1.15 = $504.85. +# NYC's 15% nontraditional differential gives $439.30 * 1.15 = $505.195, +# above the $504.85 center rate, so the district pays $504.85 * 52 / 12 +# = $2,187.68 monthly. +- name: A special-needs child's exempt group payment stops at the special-needs center rate. + period: 2025-01 + input: + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + ny_ccap_childcare_expenses: 3_000 + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + is_disabled: true + ny_ccap_special_needs_care: true + ny_ccap_nontraditional_hours: true + output: + ny_ccap_regular_market_rate: 382 * 52 / 12 + ny_ccap_special_needs_rate_eligible: true + ny_ccap_market_rate: 382 * 1.15 * 52 / 12 + ny_ccap_differential: 0.15 + ny_ccap_day_care_center_rate: 439 * 1.15 * 52 / 12 + ny_ccap_payment_rate: 439 * 1.15 * 52 / 12 + absolute_error_margin: 0.01 + # Annual expenses of $12,000 give $1,000 monthly. After the 5% differential # and one $100 family share, the family receives $1,000 * 1.05 - $100 = $950. - name: The family receives the differential before subtracting one family share. @@ -387,29 +378,114 @@ ny_ccap_payment_rate: 1_050 absolute_error_margin: 0.01 -# A district must pay at least 5% for nontraditional hours even if its -# configured percentage falls below the minimum: $1,000 * 1.05 = $1,050. -- name: The nontraditional-hours differential enforces the five-percent minimum. - period: 2025-01 +# Monroe's plan took effect November 18, 2024, so November 2024 still uses +# the state defaults. A Monroe center charges $340 weekly for a preschooler, +# $340 * 52 / 12 = $1,473.33 monthly. Homelessness and nontraditional hours +# pay 5% each, and accreditation pays nothing yet: $1,473.33 * 1.10 = +# $1,620.67. Exempt care for a homeless child pays nothing yet: $185 * 52 / 12 +# = $801.67. +- name: Monroe County pays state defaults in November 2024. + period: 2024-11 + input: + people: + center: + childcare_provider_type_group: {2024: DCC_SACC} + ny_ccap_age_group: {2024: PRESCHOOLER} + ny_ccap_duration_of_care: {2024: WEEKLY} + ny_ccap_childcare_expenses: 3_000 + ny_ccap_nontraditional_hours: {2024: true} + ny_ccap_accredited_provider: {2024: true} + exempt: + childcare_provider_type_group: {2024: LE_STD} + ny_ccap_age_group: {2024: PRESCHOOLER} + ny_ccap_duration_of_care: {2024: WEEKLY} + ny_ccap_childcare_expenses: 3_000 + households: + center: + members: [center] + state_code: {2024: NY} + county_str: {2024: MONROE_COUNTY_NY} + is_homeless: {2024: true} + exempt: + members: [exempt] + state_code: {2024: NY} + county_str: {2024: MONROE_COUNTY_NY} + is_homeless: {2024: true} + output: + ny_ccap_market_rate: [1_473.33, 801.67] + ny_ccap_differential: [0.10, 0] + ny_ccap_payment_rate: [1_620.67, 801.67] + absolute_error_margin: 0.01 + +# From December 2024, Monroe adds its 10% accreditation differential: +# 5% + 5% + 10% = 20%, and $1,473.33 * 1.20 = $1,768. +- name: Monroe County adds its accreditation differential from December 2024. + period: 2024-12 + input: + state_code: {2024: NY} + county_str: {2024: MONROE_COUNTY_NY} + childcare_provider_type_group: {2024: DCC_SACC} + ny_ccap_age_group: {2024: PRESCHOOLER} + ny_ccap_duration_of_care: {2024: WEEKLY} + ny_ccap_childcare_expenses: 3_000 + ny_ccap_nontraditional_hours: {2024: true} + ny_ccap_accredited_provider: {2024: true} + is_homeless: {2024: true} + output: + ny_ccap_differential: 0.20 + ny_ccap_payment_rate: 340 * 52 / 12 * 1.20 + absolute_error_margin: 0.01 + +# In June 2025, the 10% statutory minimum applies to homelessness and +# nontraditional hours: 10% + 10% + 10% = 30%, above the 25% limit. +# $1,473.33 * 1.25 = $1,841.67. +- name: Monroe County's combined differential stops at twenty-five percent in June 2025. + period: 2025-06 + input: + state_code: {2025: NY} + county_str: {2025: MONROE_COUNTY_NY} + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_age_group: {2025: PRESCHOOLER} + ny_ccap_duration_of_care: {2025: WEEKLY} + ny_ccap_childcare_expenses: 3_000 + ny_ccap_nontraditional_hours: {2025: true} + ny_ccap_accredited_provider: {2025: true} + is_homeless: {2025: true} + output: + ny_ccap_differential: 0.25 + ny_ccap_payment_rate: 340 * 52 / 12 * 1.25 + absolute_error_margin: 0.01 + +# OCFS raised the combined limit to 35% from December 24, 2025, so the full +# 30% applies in January 2026: $1,473.33 * 1.30 = $1,915.33. +- name: Monroe County pays the full thirty percent under the 2026 limit. + period: 2026-01 input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 state_code: NY - ny_ccap_county_group: GROUP_1 + county_str: MONROE_COUNTY_NY childcare_provider_type_group: DCC_SACC - ny_ccap_childcare_expenses: 1_200 - ny_ccap_market_rate: 1_000 + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + ny_ccap_childcare_expenses: 3_000 ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + is_homeless: true output: - ny_ccap_differential: 0.05 - ny_ccap_payment_rate: 1_050 + ny_ccap_differential: 0.30 + ny_ccap_payment_rate: 340 * 52 / 12 * 1.30 absolute_error_margin: 0.01 -# A district that elects an accreditation differential must pay at least 5%. +# District with low configured rates and a 40% waiver. +# An elected 1% accreditation rate rises to the 5% accreditation minimum: # max(.01, .05) = .05; $1,000 * 1.05 = $1,050. -- name: An elected accreditation differential enforces the five-percent minimum. +- name: An elected accreditation differential rises to the five-percent minimum. period: 2025-01 input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.40 state_code: NY ny_ccap_county_group: GROUP_1 childcare_provider_type_group: DCC_SACC @@ -421,13 +497,165 @@ ny_ccap_payment_rate: 1_050 absolute_error_margin: 0.01 -# Each configured 30% differential stops at 15%; their sum stops at 25%. -# min(.15 + .15 + .15, .25) = .25; $1,000 * 1.25 = $1,250. -- name: Individual and total maxima limit excessive configured differentials. +# District with low configured rates and a 40% waiver, January 2025. +# Configured 0% rates rise to the 5% minimum for nontraditional hours and +# licensed homelessness; the 1% accreditation rate rises to 5%. +# All three: .05 + .05 + .05 = .15; $1,000 * 1.15 = $1,150. +# Exempt homelessness has no minimum, so 2% applies: $1,000 * 1.02 = $1,020. +- name: Configured rates below the 2024 minimums rise to five percent. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.40 + people: + nontraditional: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + homeless: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + all_three: + childcare_provider_type_group: DCC_SACC + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: true + ny_ccap_accredited_provider: true + exempt_homeless: + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + households: + nontraditional: + members: [nontraditional] + state_code: NY + ny_ccap_county_group: GROUP_1 + homeless: + members: [homeless] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + all_three: + members: [all_three] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + exempt_homeless: + members: [exempt_homeless] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + output: + ny_ccap_differential: [0.05, 0.05, 0.15, 0.02] + ny_ccap_payment_rate: [1_050, 1_050, 1_150, 1_020] + absolute_error_margin: 0.01 + +# District with low configured rates and a 40% waiver, June 2025. +# Nontraditional hours and licensed homelessness rise to the 10% minimum, +# while accreditation keeps its 5% minimum. +# All three: .10 + .10 + .05 = .25; $1,000 * 1.25 = $1,250. +- name: Configured rates below the 2025 minimums rise to ten percent, except accreditation. + period: 2025-06 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.40 + people: + nontraditional: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + homeless: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + accredited: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: {2025: true} + all_three: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + ny_ccap_accredited_provider: {2025: true} + exempt_homeless: + childcare_provider_type_group: {2025: LE_STD} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + households: + nontraditional: + members: [nontraditional] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + homeless: + members: [homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + accredited: + members: [accredited] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + all_three: + members: [all_three] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + exempt_homeless: + members: [exempt_homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + output: + ny_ccap_differential: [0.10, 0.10, 0.05, 0.25, 0.02] + ny_ccap_payment_rate: [1_100, 1_100, 1_050, 1_250, 1_020] + absolute_error_margin: 0.01 + +# District with low configured rates and a 40% waiver, June 2025. +# In Monroe, nontraditional hours and homelessness rise to 10% each and +# accreditation pays Monroe's 10%: .10 + .10 + .10 = .30, within the waived +# limit, so $1,000 * 1.30 = $1,300. Without the waiver it stops at 25%. +- name: An OCFS waiver lets the district pay combined differentials above twenty-five percent. + period: 2025-06 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.40 + state_code: {2025: NY} + county_str: {2025: MONROE_COUNTY_NY} + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + ny_ccap_accredited_provider: {2025: true} + is_homeless: {2025: true} + output: + ny_ccap_differential: 0.30 + ny_ccap_payment_rate: 1_300 + absolute_error_margin: 0.01 + +# District that configures 30% for every differential, January 2025. +# Each differential stops at 15%. Exempt providers never receive the +# accreditation differential. All three: min(.15 + .15 + .15, .25) = .25, +# so a registered family provider receives $1,000 * 1.25 = $1,250. +- name: Individual and combined maxima limit excessive configured differentials. period: 2025-01 input: gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.30 gov.states.ny.ocfs.ccap.differentials.homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.30 gov.states.ny.ocfs.ccap.differentials.accreditation: 0.30 people: nontraditional: @@ -444,10 +672,19 @@ ny_ccap_childcare_expenses: 1_200 ny_ccap_market_rate: 1_000 ny_ccap_accredited_provider: true - all_three: - childcare_provider_type_group: DCC_SACC + exempt_homeless: + childcare_provider_type_group: LE_STD + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + exempt_accredited: + childcare_provider_type_group: LE_STD ny_ccap_childcare_expenses: 1_200 ny_ccap_market_rate: 1_000 + ny_ccap_accredited_provider: true + all_three: + childcare_provider_type_group: FDC_GFDC + ny_ccap_childcare_expenses: 2_000 + ny_ccap_market_rate: 1_000 ny_ccap_nontraditional_hours: true ny_ccap_accredited_provider: true households: @@ -464,25 +701,57 @@ members: [accredited] state_code: NY ny_ccap_county_group: GROUP_1 + exempt_homeless: + members: [exempt_homeless] + state_code: NY + ny_ccap_county_group: GROUP_1 + is_homeless: true + exempt_accredited: + members: [exempt_accredited] + state_code: NY + ny_ccap_county_group: GROUP_1 all_three: members: [all_three] state_code: NY ny_ccap_county_group: GROUP_1 is_homeless: true output: - ny_ccap_differential: [0.15, 0.15, 0.15, 0.25] - ny_ccap_payment_rate: [1_150, 1_150, 1_150, 1_250] + ny_ccap_differential: [0.15, 0.15, 0.15, 0.15, 0, 0.25] + ny_ccap_payment_rate: [1_150, 1_150, 1_150, 1_150, 1_000, 1_250] absolute_error_margin: 0.01 -# An OCFS waiver can raise the district's combined limit. With a 35% waived -# limit, min(.15 + .15 + .15, .35) = .35 and $1,000 * 1.35 = $1,350. -- name: An OCFS waiver allows the district to raise the combined limit. - period: 2025-01 +# District that configures 30% for every differential, December 2025. +# The 35% limit took effect December 24, 2025; the monthly model first +# reads it in January 2026. min(.45, .25) = .25; $1,000 * 1.25 = $1,250. +- name: December 2025 keeps the twenty-five percent combined limit. + period: 2025-12 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.30 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.30 + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + childcare_provider_type_group: {2025: FDC_GFDC} + ny_ccap_childcare_expenses: 2_000 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + ny_ccap_accredited_provider: {2025: true} + is_homeless: {2025: true} + output: + ny_ccap_differential: 0.25 + ny_ccap_payment_rate: 1_250 + absolute_error_margin: 0.01 + +# District that configures 30% for every differential, January 2026. +# min(.15 + .15 + .15, .35) = .35; $1,000 * 1.35 = $1,350. +- name: From January 2026 the combined differential stops at thirty-five percent. + period: 2026-01 input: - gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.15 - gov.states.ny.ocfs.ccap.differentials.homelessness: 0.15 - gov.states.ny.ocfs.ccap.differentials.accreditation: 0.15 - gov.states.ny.ocfs.ccap.differentials.maximum: 0.35 + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.30 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.30 state_code: NY ny_ccap_county_group: GROUP_1 childcare_provider_type_group: FDC_GFDC @@ -495,3 +764,32 @@ ny_ccap_differential: 0.35 ny_ccap_payment_rate: 1_350 absolute_error_margin: 0.01 + +# District that configures 30% for every differential, January 2025. +# NYC's enhanced group rate pays $382 weekly for both requirements. NYC's +# 15% nontraditional rate plus the exempt homelessness rate, capped at 15%, +# give min(.15 + .15, .25) = .25. min($3,000, $382 * 52 / 12) * 1.25 +# exceeds $439 * 52 / 12, so the district pays the center rate, $1,902.33. +- name: Exempt group payment stops at the center rate after differentials. + period: 2025-01 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0.30 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.30 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.30 + state_code: NY + ny_ccap_county_group: GROUP_4 + childcare_provider_type_group: LE_GC + ny_ccap_age_group: PRESCHOOLER + ny_ccap_duration_of_care: WEEKLY + ny_ccap_childcare_expenses: 3_000 + ny_ccap_nontraditional_hours: true + ny_ccap_enhanced_health: true + ny_ccap_enhanced_training: true + is_homeless: true + output: + ny_ccap_market_rate: 382 * 52 / 12 + ny_ccap_differential: 0.25 + ny_ccap_day_care_center_rate: 439 * 52 / 12 + ny_ccap_payment_rate: 439 * 52 / 12 + absolute_error_margin: 0.01 diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py index 370586ee435..beb0af3aab7 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_day_care_center_rate.py @@ -10,19 +10,22 @@ class ny_ccap_day_care_center_rate(Variable): unit = USD defined_for = StateCode.NY documentation = ( - "Prices the child's schedule at the regular day care center rate. " - "24-OCFS-LCM-22 caps legally exempt group payments at this amount, " - "including enhancements and differentials." + "Prices the child's schedule at the day care center market rate that " + "applies to the child. A child who qualifies for the special-needs " + "market rate gets that rate here too. 24-OCFS-LCM-22 caps legally " + "exempt group payments at this amount, including enhancements and " + "differentials." ) reference = ( - "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9" + # PDF pages 7, 9 + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=7", ) def formula(person, period, parameters): p = parameters(period).gov.states.ny.ocfs.ccap.rates county_group = person.household("ny_ccap_county_group", period.this_year) age_group = person("ny_ccap_age_group", period.this_year) - return ny_ccap_monthly_rate( + center_rate = ny_ccap_monthly_rate( person, period, parameters, @@ -30,3 +33,8 @@ def formula(person, period, parameters): p.daily[county_group]["DCC_SACC"][age_group], p.part_day[county_group]["DCC_SACC"][age_group], ) + # LCM-22 makes the special-needs rate the market rate for a + # qualifying child, so the center rate that caps group payments + # rises with it. + special_needs = person("ny_ccap_special_needs_rate_eligible", period) + return center_rate * where(special_needs, p.special_needs_multiplier, 1) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py index ad18159b949..4831e3089ac 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_differential.py @@ -9,13 +9,18 @@ class ny_ccap_differential(Variable): defined_for = StateCode.NY documentation = ( "Adds the child's nontraditional-hours, homelessness and eligible " - "provider accreditation differentials, subject to the district's " - "combined limit under 24-OCFS-LCM-22. The default limit assumes no " - "OCFS waiver. Confirmed district rates replace state defaults." + "provider accreditation differentials, subject to the combined limit. " + "From April 1, 2025, Social Services Law 410-x(10) raises the " + "nontraditional-hours and licensed or registered homelessness minimum " + "from 5% to 10%; an elected accreditation differential keeps its 5% " + "minimum. The default limit assumes no OCFS waiver. Confirmed district " + "rates replace state defaults." ) reference = ( "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + # PDF pages 9-10 "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + "https://www.nysenate.gov/legislation/laws/SOS/410-X", ) def formula(person, period, parameters): @@ -35,10 +40,13 @@ def formula(person, period, parameters): monroe, p.monroe_legally_exempt_homelessness, p.legally_exempt_homelessness ) # Optional accreditation pays zero until a district adopts it, then - # the same minimum and maximum as the mandatory differentials apply. + # its own minimum and the shared individual maximum apply. accreditation_rate = where( accreditation_rate > 0, - min_(max_(accreditation_rate, p.minimum), p.maximum_individual), + min_( + max_(accreditation_rate, p.accreditation_minimum), + p.maximum_individual, + ), 0, ) homelessness_rate = where( diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py index 466f61b9764..7e57349a322 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_market_rate.py @@ -23,10 +23,7 @@ class ny_ccap_market_rate(Variable): def formula(person, period, parameters): regular = person("ny_ccap_regular_market_rate", period) - disabled = person("is_disabled", period.this_year) - additional_care = person("ny_ccap_special_needs_care", period.this_year) - expenses = person("ny_ccap_childcare_expenses", period) - qualifies = disabled & additional_care & (expenses > regular) + qualifies = person("ny_ccap_special_needs_rate_eligible", period) multiplier = parameters( period ).gov.states.ny.ocfs.ccap.rates.special_needs_multiplier diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py index a75b877f7b3..70c8e96d64c 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_nontraditional_hours.py @@ -13,9 +13,13 @@ class ny_ccap_nontraditional_hours(Variable): "7 p.m. Monday through Friday. Any care on Saturday, Sunday, " "New Year's Day, Memorial Day, Independence Day, Labor Day, " "Thanksgiving Day, or Christmas Day also qualifies under " - "24-OCFS-LCM-22." + "24-OCFS-LCM-22. Districts pay the differential on care provided " + "during those hours. The inputs don't split a child's care by hour, " + "so the model applies the differential to the child's whole monthly " + "payment whenever a user sets this input." ) reference = ( "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9", + "https://www.nysenate.gov/legislation/laws/SOS/410-X", ) diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py index 57e8c31a7b4..4debee622da 100644 --- a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.py @@ -12,7 +12,8 @@ class ny_ccap_payment_rate(Variable): "Applies the combined differential to the lesser of the child's " "expenses and applicable market rate. Under 24-OCFS-LCM-22, the " "result can exceed the provider's charge. Legally exempt group " - "payments cannot exceed the corresponding day care center rate." + "payments cannot exceed the day care center rate that applies to " + "the child, including the special-needs rate." ) reference = ( "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", diff --git a/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_rate_eligible.py b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_rate_eligible.py new file mode 100644 index 00000000000..e6d9c8502d6 --- /dev/null +++ b/policyengine_us/variables/gov/states/ny/ocfs/ccap/rates/ny_ccap_special_needs_rate_eligible.py @@ -0,0 +1,26 @@ +from policyengine_us.model_api import * + + +class ny_ccap_special_needs_rate_eligible(Variable): + value_type = bool + entity = Person + label = "New York CCAP special-needs market rate eligible" + definition_period = MONTH + defined_for = StateCode.NY + documentation = ( + "A child with a disability receives the special-needs market rate " + "when the parent demonstrates additional care needs and the child's " + "expense share exceeds the regular rate, under 18 NYCRR 415.9 and " + "24-OCFS-LCM-22." + ) + reference = ( + "https://www.law.cornell.edu/regulations/new-york/18-NYCRR-415.9", + "https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=7", + ) + + def formula(person, period, parameters): + disabled = person("is_disabled", period.this_year) + additional_care = person("ny_ccap_special_needs_care", period.this_year) + expenses = person("ny_ccap_childcare_expenses", period) + regular = person("ny_ccap_regular_market_rate", period) + return disabled & additional_care & (expenses > regular) From 5ca8629a3fd981595f8c64637464ab13eac8b95c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sat, 14 Nov 2026 09:27:15 -0500 Subject: [PATCH 7/7] Test New York CCAP's April 2025 minimum with a 0% district rate Add an April 2025 payment case to the low-rate override set. A district that configures 0% for nontraditional hours and for licensed homelessness care pays the state minimum, 10% from April 1, 2025: $1,000 * 1.10 = $1,100. The default district rates carry their own April 2025 value, so the other April case passes when the minimum stays at 5%. End the LCM-22 reference titles in minimum.yaml and maximum_individual.yaml with the pages they cite, 9-10. In the invariant test, drop the assert that restated the payment formula, and read the special-needs multiplier from the parameters. Co-Authored-By: Claude Fable 5.2 --- .../differentials/maximum_individual.yaml | 2 +- .../ny/ocfs/ccap/differentials/minimum.yaml | 2 +- .../core/test_ny_ccap_rates_invariants.py | 11 ++---- .../ocfs/ccap/rates/ny_ccap_payment_rate.yaml | 37 +++++++++++++++++++ 4 files changed, 42 insertions(+), 10 deletions(-) diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml index 703f0764877..b2709e92509 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/maximum_individual.yaml @@ -5,7 +5,7 @@ metadata: unit: /1 label: New York CCAP maximum individual differential reference: - - title: 24-OCFS-LCM-22, differential payment rates + - title: "24-OCFS-LCM-22, differential payment rates #page 9-10" href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 - title: NY Social Services Law 410-x(10), effective April 1, 2025 href: https://www.nysenate.gov/legislation/laws/SOS/410-X diff --git a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml index 5c743aff0d0..612230292dc 100644 --- a/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml +++ b/policyengine_us/parameters/gov/states/ny/ocfs/ccap/differentials/minimum.yaml @@ -6,7 +6,7 @@ metadata: unit: /1 label: New York CCAP mandatory minimum differential reference: - - title: 24-OCFS-LCM-22, differential payment rates + - title: "24-OCFS-LCM-22, differential payment rates #page 9-10" href: https://ocfs.ny.gov/main/policies/external/2024/lcm/24-OCFS-LCM-22.pdf#page=9 - title: NY Social Services Law 410-x(10), effective April 1, 2025 href: https://www.nysenate.gov/legislation/laws/SOS/410-X diff --git a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py index fe32eb077ab..b1eece4f657 100644 --- a/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py +++ b/policyengine_us/tests/core/test_ny_ccap_rates_invariants.py @@ -311,6 +311,7 @@ def _simulate(households, parameters): @given(st.lists(household(), min_size=3, max_size=8)) def test_ny_ccap_rate_and_payment_invariants(generated): parameters = system.parameters(f"{MONTH}-01").gov.states.ny.ocfs.ccap + multiplier = parameters.rates.special_needs_multiplier simulation, rows = _simulate(_coverage_households() + generated, parameters) def calc(variable, period=MONTH): @@ -369,17 +370,11 @@ def calc(variable, period=MONTH): # A qualifying child's center rate rises to the special-needs rate. np.testing.assert_allclose( center[i], - case["center_table"] * (1.15 if special_needs[i] else 1), + case["center_table"] * (multiplier if special_needs[i] else 1), err_msg=f"group payment cap rate: {context}", **TOLERANCE, ) assert payment[i] <= center[i] + 0.01, f"group payment cap: {context}" - np.testing.assert_allclose( - payment[i], - min(min(expenses[i], market[i]) * (1 + differential[i]), center[i]), - err_msg=f"group payment: {context}", - **TOLERANCE, - ) if row["variant"] == "default": if row["group"] == "GROUP_4" and case["provider"] == "LE_ENH": # ACS's 75% informal rate intentionally exceeds main's 70%. @@ -400,7 +395,7 @@ def calc(variable, period=MONTH): **TOLERANCE, ) if row["variant"] in {"special", "special_differentials", "all"}: - expected = regular[i] * (1.15 if expenses[i] > regular[i] else 1) + expected = regular[i] * (multiplier if expenses[i] > regular[i] else 1) np.testing.assert_allclose( market[i], expected, diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml index 9dbe6ac997c..106d89242ac 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/ocfs/ccap/rates/ny_ccap_payment_rate.yaml @@ -555,6 +555,43 @@ ny_ccap_payment_rate: [1_050, 1_050, 1_150, 1_020] absolute_error_margin: 0.01 +# District with low configured rates and a 40% waiver, April 2025. +# The district configures 0%, so it pays the state minimum, 10% from +# April 1, 2025, for nontraditional hours and for licensed homelessness +# care: max(0, .10) = .10; $1,000 * 1.10 = $1,100. +- name: Configured rates below the minimum rise to ten percent in April 2025. + period: 2025-04 + input: + gov.states.ny.ocfs.ccap.differentials.nontraditional_hours: 0 + gov.states.ny.ocfs.ccap.differentials.homelessness: 0 + gov.states.ny.ocfs.ccap.differentials.legally_exempt_homelessness: 0.02 + gov.states.ny.ocfs.ccap.differentials.accreditation: 0.01 + gov.states.ny.ocfs.ccap.differentials.maximum: 0.40 + people: + nontraditional: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + ny_ccap_nontraditional_hours: {2025: true} + homeless: + childcare_provider_type_group: {2025: DCC_SACC} + ny_ccap_childcare_expenses: 1_200 + ny_ccap_market_rate: 1_000 + households: + nontraditional: + members: [nontraditional] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + homeless: + members: [homeless] + state_code: {2025: NY} + ny_ccap_county_group: {2025: GROUP_1} + is_homeless: {2025: true} + output: + ny_ccap_differential: [0.10, 0.10] + ny_ccap_payment_rate: [1_100, 1_100] + absolute_error_margin: 0.01 + # District with low configured rates and a 40% waiver, June 2025. # Nontraditional hours and licensed homelessness rise to the 10% minimum, # while accreditation keeps its 5% minimum.