diff --git a/changelog_entry.yaml b/changelog_entry.yaml index e69de29bb2d..199293c071c 100644 --- a/changelog_entry.yaml +++ b/changelog_entry.yaml @@ -0,0 +1,4 @@ +- bump: minor + changes: + added: + - Add NY tax calculation for filters with AGI above $25 million integration test. diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/integration.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/integration.yaml new file mode 100644 index 00000000000..536e66c337a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/integration.yaml @@ -0,0 +1,34 @@ +- name: 36828-NY.yaml + absolute_error_margin: 2 + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 31_920_562 + ssi: 0 + wic: 0 + head_start: 0 + early_head_start: 0 + commodity_supplemental_food_program: 0 + taxable_interest_income: 1_519_734 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + premium_tax_credit: 0 + local_income_tax: 0 + state_sales_tax: 0 + spm_units: + spm_unit: + members: [person1] + snap: 0 + tanf: 0 + free_school_meals: 0 + reduced_price_school_meals: 0 + households: + household: + members: [person1] + state_fips: 36 + output: + ny_income_tax: 3_644_120 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/ny_supplemental_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/ny_supplemental_tax.yaml index 5432e584aa5..50f55fb03f2 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/ny_supplemental_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ny/tax/income/ny_supplemental_tax.yaml @@ -64,7 +64,7 @@ # 2021 NY Form IT-201 Instructions Tax computation worksheet 5 - name: Unit test 6 for 2021 New York supplemental tax married filing jointly and qualifying surviving spouse - absolute_error_margin: 0.1 + absolute_error_margin: 2 period: 2021 input: state_code: NY @@ -129,13 +129,13 @@ # 2021 NY Form IT-201 Instructions Tax computation worksheet 10 - name: Unit test 5 for 2021 New York supplemental tax single and married filling separately filer - absolute_error_margin: 0.1 + absolute_error_margin: 2 period: 2021 input: state_code: NY filing_status: SINGLE ny_agi: 30_000_000 # AGI > $25,000,000 - ny_taxable_income: 21_000_000 + ny_taxable_income: 21_000_000 ny_main_income_tax: 2_098_682.5 output: ny_supplemental_tax: 190_317.5 # 21,000,000 * 10.9% - 2,098,682.5 @@ -194,13 +194,13 @@ # 2021 NY Form IT-201 Instructions Tax computation worksheet 15 - name: Unit test 5 for 2021 New York supplemental tax head of household filer - absolute_error_margin: 0.1 + absolute_error_margin: 2 period: 2021 - input: + input: state_code: NY - filing_status: HEAD_OF_HOUSEHOLD + filing_status: HEAD_OF_HOUSEHOLD ny_agi: 30_000_000 # AGI > $25,000,000 - ny_taxable_income: 28_000_000 + ny_taxable_income: 28_000_000 ny_main_income_tax: 2_822_096.8 output: ny_supplemental_tax: 229_903.2 # 28,000,000 * 10.9% - 2,822,096.8 @@ -295,9 +295,23 @@ period: 2028 input: state_code: NY - filing_status: SEPARATE + filing_status: SEPARATE ny_agi: 220_000 ny_taxable_income: 200_000 ny_main_income_tax: 11_963.755 output: ny_supplemental_tax: 568 + +- name: Unit test for 2024 single filer with AGI above $25M (flat 10.9% rate) + absolute_error_margin: 2 + period: 2024 + input: + state_code: NY + filing_status: SINGLE + ny_agi: 33_440_296 + ny_taxable_income: 33_432_296 + ny_main_income_tax: 3_429_049.71 + output: + # For AGI > $25M, supplemental tax = (taxable_income * 10.9%) - main_tax + # = 33_432_296 * 0.109 - 3_429_049.71 = 215_070.55 + ny_supplemental_tax: 215_070.55 diff --git a/policyengine_us/variables/gov/states/ny/tax/income/ny_supplemental_tax.py b/policyengine_us/variables/gov/states/ny/tax/income/ny_supplemental_tax.py index 8daa26f4679..cac8a72979e 100644 --- a/policyengine_us/variables/gov/states/ny/tax/income/ny_supplemental_tax.py +++ b/policyengine_us/variables/gov/states/ny/tax/income/ny_supplemental_tax.py @@ -1,5 +1,18 @@ from policyengine_us.model_api import * from policyengine_core.taxscales import MarginalRateTaxScale +import numpy as np + + +def get_last_finite_threshold(scale): + """Get the last non-infinity threshold from a tax scale. + + For NY tax scales, the last threshold may be infinity for certain years. + This function returns the last finite threshold value instead. + """ + thresholds = scale.thresholds + if np.isinf(thresholds[-1]): + return thresholds[-2] + return thresholds[-1] class ny_supplemental_tax(Variable): @@ -53,20 +66,18 @@ def formula(tax_unit, period, parameters): applicable_amount / p.phase_in_length, ) - # edge case for high agi - agi_limit = select( + # For AGI above the high threshold, apply flat top rate to all income + # Get the last finite threshold from each scale (handles infinity in 2022+) + high_agi_threshold = select( in_each_status, - [ - single.thresholds[-1], - joint.thresholds[-1], - hoh.thresholds[-1], - surviving_spouse.thresholds[-1], - separate.thresholds[-1], - ], + [get_last_finite_threshold(scale) for scale in scales], ) + # Create array for marginal_rates lookup + # Use +100 for float32 precision at $25M threshold + high_agi_lookup = ny_agi * 0 + high_agi_threshold + 100 high_agi_rate = select( in_each_status, - [scale.marginal_rates(agi_limit + 1) for scale in scales], + [scale.marginal_rates(high_agi_lookup) for scale in scales], ) supplemental_tax_high_agi = ( @@ -105,13 +116,13 @@ def formula(tax_unit, period, parameters): ) return where( - ny_agi > agi_limit, + ny_agi > high_agi_threshold, supplemental_tax_high_agi, supplemental_tax_general, ) return where( - ny_agi > agi_limit, + ny_agi > high_agi_threshold, supplemental_tax_high_agi, 0, ) diff --git a/uv.lock b/uv.lock index f260826736c..258a9c9c71f 100644 --- a/uv.lock +++ b/uv.lock @@ -1519,7 +1519,7 @@ wheels = [ [[package]] name = "policyengine-us" -version = "1.465.3" +version = "1.465.4" source = { editable = "." } dependencies = [ { name = "microdf-python" },