diff --git a/changelog.d/fix-qbid-section-199a-net-capital-gain.fixed.md b/changelog.d/fix-qbid-section-199a-net-capital-gain.fixed.md new file mode 100644 index 00000000000..895c590e496 --- /dev/null +++ b/changelog.d/fix-qbid-section-199a-net-capital-gain.fixed.md @@ -0,0 +1 @@ +The qualified business income deduction's income limit subtracts net capital gain plus qualified dividends as Form 8995 line 12 defines them (keeping section 1250 and 28-percent rate gain, unaffected by a Form 4952 election, and counting only the head's and spouse's gains), rather than adjusted net capital gain. diff --git a/policyengine_us/tests/core/test_section_199a_net_capital_gain_invariants.py b/policyengine_us/tests/core/test_section_199a_net_capital_gain_invariants.py new file mode 100644 index 00000000000..3425312fbf1 --- /dev/null +++ b/policyengine_us/tests/core/test_section_199a_net_capital_gain_invariants.py @@ -0,0 +1,216 @@ +"""Invariants for the net capital gain in the QBI deduction's income limit. + +26 USC 199A(a)(2)(B) subtracts net capital gain from taxable income, and +Treas. Reg. 1.199A-1(b)(3) defines it as "net capital gain as defined in +section 1222(11) plus any qualified dividend income". Form 8995 line 12 takes +it from Schedule D lines 15 and 16 and Form 1040 line 3a, none of which a +Form 4952 investment income election reduces. A tax unit dependent's gains are +on the dependent's own return. + +The YAML cases pin specific returns. This test checks, for random gains, +losses, distributions, dividends, section 1250 and collectibles gain and +elections, in batches of tax units with and without spouses and dependents, +run as vectorized simulations with the dependents' amounts as drawn, with +them set to zero, and with no election: + +1. Oracle: `section_199a_net_capital_gain` equals Schedule D's + max(0, min(line 15, line 16)) plus nonnegative qualified dividends, summed + from the head's and spouse's inputs in numpy. +2. Election invariance: the election changes neither the gain nor the QBI + deduction. +3. Dependent invariance: dependents' gains, losses and dividends change + neither the gain nor the QBI deduction. +4. Cap and floor: the deduction is never negative and never above 20% of + taxable income less the gain, or the 199A(i) minimum when larger. +""" + +import numpy as np +from hypothesis import HealthCheck, given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation + +TOLERANCE = 0.01 # dollars +GAIN_INPUTS = [ + "long_term_capital_gains", + "short_term_capital_gains", + "non_sch_d_capital_gains", + "qualified_dividend_income", + "long_term_capital_gains_on_collectibles", +] +HEAD = dict( + is_tax_unit_head=True, is_tax_unit_spouse=False, is_tax_unit_dependent=False +) +SPOUSE = dict( + is_tax_unit_head=False, is_tax_unit_spouse=True, is_tax_unit_dependent=False +) +DEPENDENT = dict( + is_tax_unit_head=False, is_tax_unit_spouse=False, is_tax_unit_dependent=True +) + + +def _maybe(strategy): + return st.one_of(st.just(0.0), strategy) + + +signed = st.integers(-60_000, 120_000).map(float) +positive = st.integers(1, 60_000).map(float) + + +@st.composite +def person_amounts(draw): + long_term = draw(_maybe(signed)) + return { + "long_term_capital_gains": long_term, + "short_term_capital_gains": draw(_maybe(signed)), + "non_sch_d_capital_gains": draw(_maybe(st.integers(1, 5_000).map(float))), + "qualified_dividend_income": draw(_maybe(positive)), + # Collectibles gain is part of the long-term gain. + "long_term_capital_gains_on_collectibles": ( + draw(_maybe(st.integers(0, int(max(0, long_term))).map(float))) + ), + "qbid_amount": draw(_maybe(positive)), + "qualified_business_income": draw(_maybe(positive)), + } + + +@st.composite +def tax_units(draw): + kind = draw(st.sampled_from(["single", "joint", "joint_dependent"])) + return { + "head": draw(person_amounts()), + "spouse": draw(person_amounts()) if kind != "single" else None, + "dependent": draw(person_amounts()) if kind == "joint_dependent" else None, + "taxable_income_less_qbid": draw(st.integers(0, 600_000).map(float)), + "unrecaptured_section_1250_gain": draw(_maybe(positive)), + "election": draw(_maybe(st.integers(1, 80_000).map(float))), + } + + +def _situation(units, year, *, zero_dependents=False, zero_election=False): + people, tax_units_, households, marital_units = {}, {}, {}, {} + for i, u in enumerate(units): + members = [] + for role, flags in (("head", HEAD), ("spouse", SPOUSE)): + if u[role] is not None: + name = f"{role}_{i}" + people[name] = {"age": 45, **flags, **u[role]} + members.append(name) + marital_units[f"couple_{i}"] = {"members": list(members)} + if u["dependent"] is not None: + name = f"dependent_{i}" + amounts = dict(u["dependent"]) + if zero_dependents: + amounts.update({k: 0.0 for k in GAIN_INPUTS}) + people[name] = {"age": 16, **DEPENDENT, **amounts} + members.append(name) + marital_units[f"single_{i}"] = {"members": [name]} + tax_units_[f"tax_unit_{i}"] = { + "members": members, + "taxable_income_less_qbid": u["taxable_income_less_qbid"], + "unrecaptured_section_1250_gain": u["unrecaptured_section_1250_gain"], + } + # The election is the head's input. + people[f"head_{i}"]["investment_income_elected_form_4952"] = ( + 0.0 if zero_election else u["election"] + ) + households[f"household_{i}"] = {"members": members, "state_code": "TX"} + + def by_year(values): + return {k: (v if k == "members" else {year: v}) for k, v in values.items()} + + return { + "people": {k: by_year(v) for k, v in people.items()}, + "tax_units": {k: by_year(v) for k, v in tax_units_.items()}, + "households": {k: by_year(v) for k, v in households.items()}, + "marital_units": {k: by_year(v) for k, v in marital_units.items()}, + } + + +def _run(units, year, **kwargs): + sim = Simulation(situation=_situation(units, year, **kwargs)) + out = { + name: np.asarray(sim.calculate(name, year), dtype=float) + for name in [ + "section_199a_net_capital_gain", + "qualified_business_income_deduction", + "taxable_income_less_qbid", + *GAIN_INPUTS, + ] + } + out["filer"] = ~np.asarray(sim.calculate("is_tax_unit_dependent", year), bool) + out["unit"] = sim.populations["tax_unit"].members_entity_id + out["p"] = sim.tax_benefit_system.parameters(year).gov.irs.deductions.qbi + return out + + +def _filer_sum(run, name): + n = len(run["section_199a_net_capital_gain"]) + return np.bincount(run["unit"], weights=run[name] * run["filer"], minlength=n) + + +def _check(units, year): + run = _run(units, year) + gain = run["section_199a_net_capital_gain"] + deduction = run["qualified_business_income_deduction"] + + # 1. Oracle: Schedule D lines 15 and 16, and Form 1040 line 3a. + line_15 = _filer_sum(run, "long_term_capital_gains") + _filer_sum( + run, "non_sch_d_capital_gains" + ) + line_16 = line_15 + _filer_sum(run, "short_term_capital_gains") + dividends = np.maximum(0, _filer_sum(run, "qualified_dividend_income")) + oracle = np.maximum(0, np.minimum(line_15, line_16)) + dividends + np.testing.assert_allclose(gain, oracle, atol=TOLERANCE) + + # 2. Election invariance. + no_election = _run(units, year, zero_election=True) + for name in [ + "section_199a_net_capital_gain", + "qualified_business_income_deduction", + ]: + np.testing.assert_allclose( + run[name], no_election[name], atol=TOLERANCE, err_msg=name + ) + + # 3. Dependent invariance. + no_dependents = _run(units, year, zero_dependents=True) + for name in [ + "section_199a_net_capital_gain", + "qualified_business_income_deduction", + ]: + np.testing.assert_allclose( + run[name], no_dependents[name], atol=TOLERANCE, err_msg=name + ) + + # 4. Cap and floor. + p = run["p"] + cap = p.max.rate * np.maximum(0, run["taxable_income_less_qbid"] - gain) + ceiling = cap + if p.deduction_floor.in_effect: + # The largest minimum the scale gives. + ceiling = np.maximum(cap, p.deduction_floor.amount.calc(np.array([1e12]))[0]) + assert (deduction >= -TOLERANCE).all() + assert (deduction <= ceiling + TOLERANCE).all() + + +# Each example is one vectorized batch of tax units. +SETTINGS = dict( + max_examples=3, + deadline=None, + derandomize=True, + suppress_health_check=[HealthCheck.too_slow, HealthCheck.data_too_large], +) + + +@settings(**SETTINGS) +@given(st.lists(tax_units(), min_size=10, max_size=40)) +def test_section_199a_gain_and_cap_2025(units): + _check(units, 2025) + + +@settings(**SETTINGS) +@given(st.lists(tax_units(), min_size=10, max_size=40)) +def test_section_199a_gain_and_cap_2026(units): + # 2026 adds the 199A(i) minimum deduction. + _check(units, 2026) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/qualified_business_income_deduction.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/qualified_business_income_deduction.yaml index 4feef0daf86..cb7e7713312 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/qualified_business_income_deduction.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/qualified_business_income_deduction.yaml @@ -3,7 +3,7 @@ input: qbid_amount: 10_000 taxable_income_less_qbid: 30_000 - adjusted_net_capital_gain: 2_000 + section_199a_net_capital_gain: 2_000 output: qualified_business_income_deduction: 5_600 @@ -12,7 +12,7 @@ input: qbid_amount: 3_000 taxable_income_less_qbid: 30_000 - adjusted_net_capital_gain: 2_000 + section_199a_net_capital_gain: 2_000 output: qualified_business_income_deduction: 3_000 @@ -21,7 +21,7 @@ input: qbid_amount: 10_000 taxable_income_less_qbid: 30_000 - adjusted_net_capital_gain: 32_000 + section_199a_net_capital_gain: 32_000 output: qualified_business_income_deduction: 0 @@ -31,7 +31,7 @@ input: qbid_amount: 200 taxable_income_less_qbid: 600 - adjusted_net_capital_gain: 0 + section_199a_net_capital_gain: 0 qualified_business_income: 1_000 output: qualified_business_income_deduction: 400 @@ -42,7 +42,7 @@ input: qbid_amount: 200 taxable_income_less_qbid: 0 - adjusted_net_capital_gain: 0 + section_199a_net_capital_gain: 0 qualified_business_income: 999 output: qualified_business_income_deduction: 0 @@ -53,7 +53,7 @@ input: qbid_amount: 2_000 taxable_income_less_qbid: 20_000 - adjusted_net_capital_gain: 0 + section_199a_net_capital_gain: 0 qualified_business_income: 1_000 output: qualified_business_income_deduction: 2_000 @@ -74,7 +74,7 @@ tax_unit: members: [person1] taxable_income_less_qbid: 600 - adjusted_net_capital_gain: 0 + section_199a_net_capital_gain: 0 families: family: members: [person1] @@ -159,3 +159,151 @@ # Below the $197,300 single threshold so the SSTB phaseout does not apply. # SSTB QBI component = 0.20 * $30,000 = $6,000. qualified_business_income_deduction: 6_000 + +# 26 U.S.C. 199A(a)(2) subtracts "the net capital gain (as defined in section +# 1(h))" from taxable income, which is net capital gain plus qualified +# dividends (2025 Form 8995 line 12), not adjusted net capital gain. Unrecaptured +# section 1250 gain and 28-percent rate gain stay in the subtraction. +- name: Income limitation binds with unrecaptured section 1250 gain + period: 2025 + input: + qbid_amount: 20_000 + taxable_income_less_qbid: 100_000 + long_term_capital_gains: 40_000 + unrecaptured_section_1250_gain: 30_000 + output: + # Form 8995 line 10: QBI deduction before the income limitation = $20,000. + # Line 11: taxable income before the QBI deduction = $100,000. + # Line 12: net capital gain plus qualified dividends = $40,000. + # Line 13: $100,000 - $40,000 = $60,000. + # Line 14: income limitation = 20% x $60,000 = $12,000. + # Line 15: smaller of line 10 or line 14 = $12,000. + # (Subtracting adjusted net capital gain, $10,000, would allow $18,000.) + qualified_business_income_deduction: 12_000 + +- name: Income limitation binds with 28-percent rate gain + period: 2025 + input: + qbid_amount: 30_000 + taxable_income_less_qbid: 150_000 + long_term_capital_gains: 50_000 + long_term_capital_gains_on_collectibles: 50_000 + qualified_dividend_income: 10_000 + output: + # Form 8995 line 10 = $30,000; line 11 = $150,000. + # Line 12 = $10,000 (Form 1040 line 3a) + $50,000 (Schedule D) = $60,000. + # Line 13 = $90,000. Line 14 = 20% x $90,000 = $18,000. + # Line 15 = smaller of $30,000 or $18,000 = $18,000. + # (Subtracting adjusted net capital gain, $10,000, would allow $28,000.) + qualified_business_income_deduction: 18_000 + +- name: Income limitation ignores the Form 4952 investment income election + period: 2025 + input: + qbid_amount: 20_000 + taxable_income_less_qbid: 100_000 + long_term_capital_gains: 50_000 + qualified_dividend_income: 10_000 + investment_income_elected_form_4952: 20_000 + output: + # Form 8995 line 12 = $10,000 (line 3a, not reduced by Form 4952 line 4g) + # + $50,000 (smaller of Schedule D lines 15 and 16) = $60,000. + # Line 13 = $40,000. Line 14 = 20% x $40,000 = $8,000. Line 15 = $8,000. + qualified_business_income_deduction: 8_000 + +- name: Self-employed seller of depreciated rental property + absolute_error_margin: 0.01 + period: 2025 + input: + people: + person1: + age: 45 + self_employment_income: 80_000 + long_term_capital_gains: 50_000 + # Zero out QBI deductions so QBI equals self-employment income. + self_employment_tax_ald_person: 0 + self_employed_health_insurance_ald_person: 0 + self_employed_pension_contribution_ald_person: 0 + is_tax_unit_head: true + tax_units: + tax_unit: + members: [person1] + # All of the long-term gain is unrecaptured section 1250 gain. + unrecaptured_section_1250_gain: 50_000 + families: + family: + members: [person1] + spm_units: + spm_unit: + members: [person1] + households: + household: + members: [person1] + state_code: TX + output: + # AGI = $80,000 + $50,000 = $130,000; 2025 single standard deduction + # $15,750. + # Form 8995 line 1c/2 = $80,000 of QBI; line 4 = $80,000; + # line 5 = 20% x $80,000 = $16,000; line 10 = $16,000. + # Line 11 = $130,000 - $15,750 = $114,250 (below the $197,300 threshold). + taxable_income_less_qbid: 114_250 + # Line 12 = $50,000 of net capital gain (Schedule D lines 15 and 16). + section_199a_net_capital_gain: 50_000 + # Line 13 = $64,250. Line 14 = 20% x $64,250 = $12,850. + # Line 15 = smaller of $16,000 or $12,850 = $12,850. + # (Adjusted net capital gain is $0, which would leave line 10's $16,000.) + adjusted_net_capital_gain: 0 + qualified_business_income_deduction: 12_850 + # Form 1040 line 15 = $114,250 - $12,850 = $101,400. + taxable_income: 101_400 + +# A tax unit dependent's gains and dividends are on the dependent's own +# return, so they neither shrink nor widen the filer's income limitation. +- name: Dependent gains do not reduce the parent's QBI deduction + period: 2025 + input: + people: + head: + is_tax_unit_head: true + qbid_amount: 20_000 + long_term_capital_gains: 40_000 + short_term_capital_gains: -10_000 + qualified_dividend_income: 5_000 + child: + is_tax_unit_dependent: true + qbid_amount: 0 + long_term_capital_gains: 200_000 + qualified_dividend_income: 50_000 + tax_units: + unit: + members: [head, child] + taxable_income_less_qbid: 100_000 + output: + # Parent: smaller of Schedule D lines 15 (40,000) and 16 (30,000), plus + # 5,000 of qualified dividends = 35,000. + section_199a_net_capital_gain: 35_000 + # 20% x (100,000 - 35,000) = 13,000, below the 20,000 QBI amount. + qualified_business_income_deduction: 13_000 + +- name: Dependent losses do not increase the parent's QBI deduction + period: 2025 + input: + people: + head: + is_tax_unit_head: true + qbid_amount: 20_000 + long_term_capital_gains: 40_000 + qualified_dividend_income: 5_000 + child: + is_tax_unit_dependent: true + qbid_amount: 0 + short_term_capital_gains: -100_000 + tax_units: + unit: + members: [head, child] + taxable_income_less_qbid: 100_000 + output: + # Parent: 40,000 + 5,000 = 45,000; the child's loss is not netted. + section_199a_net_capital_gain: 45_000 + # 20% x (100,000 - 45,000) = 11,000. + qualified_business_income_deduction: 11_000 diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/section_199a_net_capital_gain.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/section_199a_net_capital_gain.yaml new file mode 100644 index 00000000000..9aeaefef571 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/deductions/qbid/section_199a_net_capital_gain.yaml @@ -0,0 +1,142 @@ +# Net capital gain for the 26 U.S.C. 199A(a)(2) income limitation: 2025 Form +# 8995 line 12 (Form 8995-A line 34), "Form 1040 ... line 3a, plus your net +# capital gain", where net capital gain is "the smaller of Schedule D (Form +# 1040), line 15 or 16, unless line 15 or 16 is zero or less", or "Form 1040 +# ... line 7a" without Schedule D. Treas. Reg. 1.199A-1(b)(3): "net capital gain +# as defined in section 1222(11) plus any qualified dividend income". + +- name: Unrecaptured section 1250 gain stays in net capital gain + period: 2025 + input: + long_term_capital_gains: 40_000 + unrecaptured_section_1250_gain: 30_000 + output: + # Schedule D line 15 = line 16 = $40,000; Form 1040 line 3a = $0. + # Form 8995 line 12 = $0 + $40,000 = $40,000. + section_199a_net_capital_gain: 40_000 + # 26 U.S.C. 1(h)(3) takes the 1250 gain out: $40,000 - $30,000. + adjusted_net_capital_gain: 10_000 + +- name: 28-percent rate gain stays in net capital gain + period: 2025 + input: + long_term_capital_gains: 50_000 + long_term_capital_gains_on_collectibles: 50_000 + qualified_dividend_income: 10_000 + output: + # Schedule D line 15 = line 16 = $50,000, all collectibles gain (line 18). + # Form 8995 line 12 = $10,000 (line 3a) + $50,000 = $60,000. + section_199a_net_capital_gain: 60_000 + # 26 U.S.C. 1(h)(3): ($50,000 - $50,000) + $10,000 of dividends. + adjusted_net_capital_gain: 10_000 + +- name: Short-term loss nets against long-term gain + period: 2025 + input: + long_term_capital_gains: 30_000 + short_term_capital_gains: -10_000 + qualified_dividend_income: 5_000 + output: + # Schedule D line 7 = -$10,000; line 15 = $30,000; + # line 16 = -$10,000 + $30,000 = $20,000. Smaller of 15 or 16 = $20,000. + # Form 8995 line 12 = $5,000 + $20,000 = $25,000. + section_199a_net_capital_gain: 25_000 + +- name: Short-term gain is not net capital gain + period: 2025 + input: + long_term_capital_gains: 30_000 + short_term_capital_gains: 8_000 + output: + # Schedule D line 15 = $30,000; line 16 = $38,000. Smaller = $30,000. + section_199a_net_capital_gain: 30_000 + +- name: Combined loss leaves only qualified dividends + period: 2025 + input: + long_term_capital_gains: 10_000 + short_term_capital_gains: -15_000 + qualified_dividend_income: 2_000 + output: + # Schedule D line 15 = $10,000; line 16 = -$5,000, zero or less, so + # "nothing is added to the qualified dividends". Line 12 = $2,000. + section_199a_net_capital_gain: 2_000 + +- name: Long-term loss leaves only qualified dividends + period: 2025 + input: + long_term_capital_gains: -5_000 + short_term_capital_gains: 20_000 + qualified_dividend_income: 1_000 + output: + # Schedule D line 15 = -$5,000, zero or less. Line 12 = $1,000. + section_199a_net_capital_gain: 1_000 + +- name: Capital gain distributions without Schedule D + period: 2025 + input: + non_sch_d_capital_gains: 8_000 + qualified_dividend_income: 2_000 + output: + # Not required to file Schedule D: net capital gain is Form 1040 line 7a. + # Form 8995 line 12 = $2,000 + $8,000 = $10,000. + section_199a_net_capital_gain: 10_000 + +- name: Form 4952 investment income election does not reduce it + period: 2025 + input: + long_term_capital_gains: 50_000 + qualified_dividend_income: 10_000 + investment_income_elected_form_4952: 20_000 + output: + # Form 8995 line 12 uses Form 1040 line 3a ($10,000), which the Form 4952 + # line 4g instructions say not to reduce, plus the smaller of Schedule D + # lines 15 and 16 ($50,000). Treas. Reg. 1.199A-1(b)(3) uses section + # 1222(11) net capital gain and section 1(h)(11)(B) qualified dividends. + section_199a_net_capital_gain: 60_000 + # 26 U.S.C. 1(h)(2) takes the $20,000 elected gain out for section 1(h). + net_capital_gain: 40_000 + +- name: Gains and dividends of both spouses + period: 2025 + input: + people: + head: + long_term_capital_gains: 20_000 + spouse: + short_term_capital_gains: -5_000 + qualified_dividend_income: 3_000 + tax_units: + tax_unit: + members: [head, spouse] + output: + # Schedule D line 15 = $20,000; line 16 = $15,000. Line 3a = $3,000. + # Form 8995 line 12 = $3,000 + $15,000 = $18,000. + section_199a_net_capital_gain: 18_000 + +- name: A negative qualified dividend input adds nothing + period: 2025 + input: + long_term_capital_gains: 10_000 + qualified_dividend_income: -2_000 + output: + # Form 1040 line 3a cannot be negative; the gain is not reduced. + section_199a_net_capital_gain: 10_000 + +- name: Spouses' qualified dividends are summed before the zero floor + period: 2025 + input: + people: + head: + is_tax_unit_head: true + long_term_capital_gains: 10_000 + qualified_dividend_income: 5_000 + spouse: + is_tax_unit_spouse: true + qualified_dividend_income: -2_000 + tax_units: + unit: + members: [head, spouse] + output: + # 10,000 + max(0, 5,000 - 2,000). + section_199a_net_capital_gain: 13_000 diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/qualified_business_income_deduction.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/qualified_business_income_deduction.py index 2703ee6f05c..6d7c6bbbb51 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/qualified_business_income_deduction.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/qualified_business_income_deduction.py @@ -8,8 +8,11 @@ class qualified_business_income_deduction(Variable): unit = USD definition_period = YEAR reference = ( + "https://www.law.cornell.edu/uscode/text/26/199A#a", "https://www.law.cornell.edu/uscode/text/26/199A#b_1", "https://www.irs.gov/pub/irs-prior/p535--2018.pdf", + "https://www.irs.gov/pub/irs-prior/f8995--2025.pdf", + "https://www.irs.gov/pub/irs-prior/i8995--2025.pdf#page=4", ) def formula(tax_unit, period, parameters): @@ -23,10 +26,11 @@ def formula(tax_unit, period, parameters): filer = ~person("is_tax_unit_dependent", period) qbid_amt = person("qbid_amount", period) uncapped_qbid = tax_unit.sum(qbid_amt * filer) - # apply taxinc cap at the TaxUnit level following logic - # in 2018 IRS Publication 535, Worksheet 12-A, lines 32-37 + # apply taxinc cap at the TaxUnit level following 26 U.S.C. + # 199A(a)(2): 20 percent of taxable income over net capital gain + # (2025 Form 8995 lines 11-14, Form 8995-A lines 33-36) taxinc_less_qbid = tax_unit("taxable_income_less_qbid", period) - netcg_qdiv = tax_unit("adjusted_net_capital_gain", period) + netcg_qdiv = tax_unit("section_199a_net_capital_gain", period) p = parameters(period).gov.irs.deductions.qbi taxinc_cap = p.max.rate * max_(0, taxinc_less_qbid - netcg_qdiv) pre_floor_qbid = min_(uncapped_qbid, taxinc_cap) diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/section_199a_net_capital_gain.py b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/section_199a_net_capital_gain.py new file mode 100644 index 00000000000..f0ca6fb51f5 --- /dev/null +++ b/policyengine_us/variables/gov/irs/income/taxable_income/deductions/qualified_business_income_deduction/section_199a_net_capital_gain.py @@ -0,0 +1,71 @@ +from policyengine_us.model_api import * + + +class section_199a_net_capital_gain(Variable): + value_type = float + entity = TaxUnit + label = "Net capital gain for the qualified business income deduction" + unit = USD + documentation = ( + "Net capital gain under 26 U.S.C. 1222(11) plus qualified dividend " + "income, the amount subtracted from taxable income in the 26 U.S.C. " + "199A(a)(2) income limitation (Form 8995 line 12, Form 8995-A line " + "34). Unlike adjusted net capital gain, it keeps unrecaptured section " + "1250 gain and 28-percent rate gain. Unlike net_capital_gain, it is " + "not reduced by a Form 4952 investment income election. It counts the " + "head's and spouse's gains and dividends only: a tax unit dependent's " + "are on the dependent's own return, as taxable income already leaves " + "them off this one." + ) + definition_period = YEAR + reference = [ + dict( + title="26 U.S. Code § 199A(a)(2)(B)", + href="https://www.law.cornell.edu/uscode/text/26/199A#a_2_B", + ), + dict( + title="26 CFR § 1.199A-1(b)(3)", + href="https://www.ecfr.gov/current/title-26/part-1/section-1.199A-1#p-1.199A-1(b)(3)", + ), + dict( + title="26 U.S. Code § 1222(11)", + href="https://www.law.cornell.edu/uscode/text/26/1222#11", + ), + dict( + title="2025 Instructions for Form 8995, line 12", + href="https://www.irs.gov/pub/irs-prior/i8995--2025.pdf#page=4", + ), + dict( + title="2025 Instructions for Form 8995-A, line 34", + href="https://www.irs.gov/pub/irs-prior/i8995a--2025.pdf#page=7", + ), + ] + + def formula(tax_unit, period, parameters): + # Treas. Reg. 1.199A-1(b)(3): "net capital gain as defined in section + # 1222(11) plus any qualified dividend income (as defined in section + # 1(h)(11)(B))". Neither part is reduced by a Form 4952 line 4g + # election: the form uses Form 1040 line 3a, which the Form 4952 + # instructions say not to reduce, and Schedule D lines 15 and 16. + # Schedule D line 15: net long-term gain, including capital gain + # distributions (long-term under 26 U.S.C. 852(b)(3)(B)). Without + # Schedule D, net capital gain is Form 1040 line 7a. + # Dependents' amounts are left out before netting, so a dependent's + # loss never offsets the filer's gain. + long_term_gain = tax_unit_non_dep_add( + tax_unit, + period, + ["long_term_capital_gains", "non_sch_d_capital_gains"], + ) + # Schedule D line 16: line 7 (net short-term gain) plus line 15. + combined_gain = long_term_gain + tax_unit_non_dep_add( + tax_unit, period, ["short_term_capital_gains"] + ) + # "The smaller of Schedule D (Form 1040), line 15 or 16, unless line + # 15 or 16 is zero or less, in which case nothing is added to the + # qualified dividends." + net_capital_gain = max_(0, min_(long_term_gain, combined_gain)) + qualified_dividends = max_( + 0, tax_unit_non_dep_add(tax_unit, period, ["qualified_dividend_income"]) + ) + return net_capital_gain + qualified_dividends