Should the emulator assume sufficient W-2 wages for the QBI deduction, as TAXSIM does?

Closed
#1,404 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

Nobody has claimed this yet.

Assessment

Difficulty
4/5
Estimated time
3-5 days
Newbie friendliness
35/100
Issue type
Feature
Clarity
Mostly clear
Activity status
Active
Tech stack
python
Domain
backend

Research direction

Read the stdin drop-in command and the --assume-w2-wages handling in the policyengine and compare subcommands; the issue names no file or test. First determine how stdin options are parsed and how QBI results are compared with TAXSIM. Done means an explicit decision on whether the default should change, with the stdin flag behavior and tests matching that decision.

Written by the indexing model from the issue text.

Description

The stdin drop-in command (policyengine-taxsim < input.csv) gives a QBI deduction of 0 to a single filer with $400,000 of pbusinc in 2024. The bundled taxsimtest binary gives 77,080. --assume-w2-wages closes most of the gap, but only the policyengine and compare subcommands accept it.

Reproduce

policyengine-taxsim 3.0.1 (3e2a7589), policyengine-us 2.30.1, bundled taxsimtest-osx.exe:

taxsimid,year,state,mstat,page,psemp,pbusinc,idtl
1,2024,0,1,40,0,400000,2
2,2024,0,1,40,400000,0,2
Row Engine fica v10 (AGI) qbid fiitax
1 pbusinc TAXSIM binary 0 400,000.00 77,080.00 85,886.75
1 pbusinc emulator, default 33,143.60 384,190.47 0 99,731.42
1 pbusinc emulator, --assume-w2-wages 33,143.60 384,190.47 73,918.10 73,860.10
2 psemp TAXSIM binary 33,143.60 384,190.50 73,918.10 73,860.09
2 psemp emulator, default 33,143.60 384,190.47 0 99,731.42
2 psemp emulator, --assume-w2-wages 33,143.60 384,190.47 73,918.10 73,860.10

With the flag, row 2 matches TAXSIM to the cent. The remaining 3,162 gap on row 1 comes from the binary's missing SECA on pbusinc (#1254), not from the wage limit.

Why the default gives 0

PolicyEngine-US applies the statutory W-2 wage limit. TAXSIM input has no W-2 wage column, so the emulator supplies none. 26 U.S.C. § 199A(b)(2), "Determination of deductible amount for each trade or business" (LII, fetched 2026-10-07):

The amount determined under this paragraph with respect to any qualified trade or business is the lesser of— (A) 20 percent of the taxpayer's qualified business income with respect to the qualified trade or business, or (B) the greater of— (i) 50 percent of the W–2 wages with respect to the qualified trade or business, or (ii) the sum of 25 percent of the W–2 wages with respect to the qualified trade or business, plus 2.5 percent of the unadjusted basis immediately after acquisition of all qualified property.

§ 199A(b)(3)(A) drops (B) only at or below the threshold amount:

In the case of any taxpayer whose taxable income for the taxable year does not exceed the threshold amount, paragraph (2) shall be applied without regard to subparagraph (B).

Rev. Proc. 2023-34, § 3.27 "Qualified Business Income" (page 15):

For taxable years beginning in 2024, the threshold amounts under § 199A(e)(2) and phase-in range amounts under § 199A(b)(3)(B) and § 199A(d)(3)(A) are: [...] All Other Returns $191,950 $241,950

Taxable income before the deduction is 369,590, above 241,950. With no W-2 wages and no qualified property, (B) is 0, so the deduction is 0.

What TAXSIM does

The taxsimtest input documentation ("Date last modified: 28 February 2026", fetched 2026-10-07), item 26:

  1. pbusinc and sbusinc Primary and secondary Taxpayer's active income eligible for the QBI deduction without phaseout and assuming sufficient wages paid or capital to be eligible for the full deduction. Subject to NIIT, SECA and Medicare additional Earnings Tax.

The full QBI deduction block of the published source (law87.for lines 796–811, feenberg/taxsim @ 359c71f) has no wage or capital term. The deduction is 20% of the QBI pools less half of SE tax, capped at ptlim:

c 2018+ pass-through income qualifies for 20% deduction
c income after fica and phaseout for sstb + scorp only
      if(lawyr.ge.2018) then
        ptqbi  =max(0.d0,d(211))+max(0.d0,d(214))
     &  +max(0.d0,d(224))+max(0d0,d(17))
        ptsst  =max(0.d0,d(212))+max(0.d0,d(215))
        ptfica =sen
        ptinc  =d(213)+ptqbi+ptsst-ptfica/2
        ptstrt =phin(lawyr,max(1,int(d(7))))
        if(sepret.eq.2.and.lawyr.eq.2019) ptstrt=160725.d0
        ptexcs =max(0d0,taxinc-ptstrt)
        perc   =min(1.d0,max(0.d0,ptexcs/50000.d0))
        ptlim  =max(0.d0,.2d0*(taxinc-(capgn+d(12))))
        ptded  =.2d0*(ptsst*(1.d0-perc)+ptqbi+d(213)-.5*sen)
        ptded  =max(0.d0,min(ptded,ptlim))
        taxinc=max(0d0,taxinc-ptded)
Questions
  1. #1405 adds --assume-w2-wages to the stdin command and keeps it off by default. Is an opt-in flag enough?
  2. Should the emulator assume sufficient W-2 wages by default, because TAXSIM documents this for pbusinc and its code has no wage limit for any QBI? That would change the default output for every QBI record above the threshold (psemp, pbusinc, scorp), so it needs an explicit, announced decision.

Side note: the same page describes psemp as "not eligible as Qualified Business Income (QBI)", but the binary gives row 2 a QBI deduction of 73,918.10. That is a separate question for TAXSIM and does not change this issue.

Dominant language
Python
Stars
18
Forks
10
Avg merge
2d 9h
Merged PRs (30d)
15

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from PolicyEngine/policyengine-taxsim

All issues in PolicyEngine/policyengine-taxsim

Similar issues

More Python issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.