Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add parameter to allow LTCG+QDIV income to be taxed as other income #973

Merged
merged 3 commits into from
Oct 7, 2016
Merged

Add parameter to allow LTCG+QDIV income to be taxed as other income #973

merged 3 commits into from
Oct 7, 2016

Conversation

martinholmer
Copy link
Collaborator

This pull request attempts to provide an easy-to-use policy parameter that causes long-term capital gains and qualified dividends to be taxed at the same rates as wage and salary income. The changes in this pull request have no effect on current-law tax results, but these changes need review to determine if they do tax calculations correctly when the new _CG_as_II parameter is set to one.

Such a reform raises 2016 income tax revenue by almost $41.9 billion. I have no idea if that is a plausible revenue increase when eliminating the special tax treatment of LTCG+QDIV
income.

tax-calculator$ cp ../tax-calculator-data/puf.csv .
tax-calculator$ python inctax.py puf.csv 2016 --weights
tax-calculator$ cat cgsame.json
{ "_CG_as_II": {"2016": [1] } }
tax-calculator$ python inctax.py puf.csv 2016 --weights --reform cgsame.json 
tax-calculator$ ll puf*
-rw-r--r--  1 mrh  staff  39385372 Oct  7 12:34 puf-16.out-inctax
-rw-r--r--  1 mrh  staff  39391761 Oct  7 12:37 puf-16.out-inctax-cgsame
-rw-r--r--@ 1 mrh  staff  52839666 Oct  7 12:32 puf.csv
tax-calculator$ awk '{rev+=$4*$29}END{print rev}' puf-16.out-inctax
9.27552e+11
tax-calculator$ awk '{rev+=$4*$29}END{print rev}' puf-16.out-inctax-cgsame 
9.69409e+11

@MattHJensen @feenberg @codykallen @Amy-Xu @GoFroggyRun @andersonfrailey

@codecov-io
Copy link

codecov-io commented Oct 7, 2016

Current coverage is 98.27% (diff: 100%)

Merging #973 into master will not change coverage

@@             master       #973   diff @@
==========================================
  Files            35         35          
  Lines          2085       2085          
  Methods           0          0          
  Messages          0          0          
  Branches          0          0          
==========================================
  Hits           2049       2049          
  Misses           36         36          
  Partials          0          0          

Powered by Codecov. Last update 591dc12...3f6f104

@codykallen
Copy link
Contributor

codykallen commented Oct 7, 2016

You could check by comparing two different reforms.
reform1 = {2016: {
'_II_rt2' = [0.1]
'_II_rt4' = [0.25]
'_II_rt5' = [0.25]
'_II_rt6' = [0.25]
'_CG_as_II' = [1]
}
}

reform2 = {2016: {
'_CG_rt1' = [0.1]
'_CG_rt2' = [0.25]
'_CG_rt3' = [0.396]
}
}

This probably won't perfectly match up, because treating ltcg and qdiv as ordinary income drops a lot of the complexity in Sch D, but the results should be close.

@codykallen
Copy link
Contributor

The changes to the CapGains and GainsTax functions look correct to me.

@MattHJensen
Copy link
Contributor

MattHJensen commented Oct 7, 2016

@martinholmer, could you help me understand this approach?

It seems to meet Cody’s pressing need — so that probably justifies pursuing it for now — but won't CG_ec_rt only have the expected effect of reducing total tax liability when CG_as_II is 1?

Another approach would be to design CG_ec_rt to work under any conditions.

If Cody wants the CG rates to match the II rates in his reform, then he can set them that way manually. If we want users to be able to tax CG as II under current law (not when there are only three brackets) then we can easily add more CG brackets, but that's an unrelated issue.

The outstanding challenge then is to design CG_ec_rt to work under any conditions. The reason it is difficult, I believe, is because of how CG and II are stacked on top of each other. If we add the exclusion in the calculation of capital gains as Cody did in his initial PR, then more income is taxed at ordinary income rates and total tax liability goes up -- that is unexpected and not good. But couldn’t we just reduce ordinary income by the right amount to offset that effect?

Maybe I am misunderstanding how your and Cody’s PRs work, though.

(I don't think this comment should hold up merging this PR to master and @codykallen getting the feature he needs.)

@martinholmer
Copy link
Collaborator Author

@MattHJensen said:

Could you help me understand this approach?

Pull request #973 and merged pull request #972 allow the specification of a Ryan-Brady-style reform in the tax treatment of investment income as follows:

(1) eliminate differential tax treatment of LTCG and QDIV income (PR#973);

(2) change the regular tax brackets and rates to 12%, 25%, and 33% (nothing new needed); and

(3) specify _ALD_Investment_ec as 0.50 (instead of 0 in current law) which reduces LTCG and QDIV income in AGI (can now be done on master because PR#972 has been merged).

This made sense to both me and @codykallen. Are we missing something?

@feenberg @Amy-Xu @GoFroggyRun @andersonfrailey

@MattHJensen
Copy link
Contributor

MattHJensen commented Oct 7, 2016

This made sense to both me and @codykallen. Are we missing something?

@martinholmer, I'm not sure yet.

Here are the results for two reforms that I think should be roughly equivalent:

reform1 = {2016: {
'_II_rt2': [0.1],
'_II_rt4': [0.25],
'_II_rt5': [0.25],
'_II_rt6': [0.25],
'_CG_as_II': [1]
}
}

-13.5 Billion.

reform2 = {2016: {
'_II_rt2': [0.1],
'_II_rt4': [0.25],
'_II_rt5': [0.25],
'_II_rt6': [0.25],
'_CG_rt1': [0.1],
'_CG_rt2': [0.25],
'_CG_rt3': [0.396],
}
}

-30.5 Billion.

@MattHJensen
Copy link
Contributor

MattHJensen commented Oct 7, 2016

@martinholmer @codykallen, I forgot about the PT_rts in the previous reforms:

reform1 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_as_II': [1]
}
}

$-23.4 B

reform2 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_rt1': [0.1],
'_CG_rt2': [0.25],
'_CG_rt3': [0.396],
}
}

$-34.8B

@codykallen
Copy link
Contributor

I think this is being distorted by the AMT. @MattHJensen can you try adding
'_AMT_trt1' = [0.0]
'_AMT_trt2' = [0.0]

@MattHJensen
Copy link
Contributor

@codykallen, good call. If AMT repeal is in both baseline and reforms:

p_yy = Policy()
reform1 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_as_II': [1],
'_AMT_trt1':[0.0],
'_AMT_trt2':[0.0]
}
}

-67.4

reform2 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_rt1': [0.1],
'_CG_rt2': [0.25],
'_CG_rt3': [0.396],
'_AMT_trt1':[0.0],
'_AMT_trt2':[0.0]
}
}

-68.8

@martinholmer

@codykallen
Copy link
Contributor

That looks pretty good to me. I think we should merge this in, and do a separate PR to adjust the AMT calculation for this.

@MattHJensen
Copy link
Contributor

MattHJensen commented Oct 7, 2016

@codykallen @martinholmer, without including AMT in the baseline, but setting the AMT_CG rates as well as the CG rates in reform 2, here are the differences:

p_yy = Policy()
reform1 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_as_II': [1],
}
}

-23.4

reform2 = {2016: {
'_II_rt2': [0.1],
'_PT_rt2': [0.1],
'_II_rt4': [0.25],
'_PT_rt4': [0.25],
'_II_rt5': [0.25],
'_PT_rt5': [0.25],
'_II_rt6': [0.25],
'_PT_rt6': [0.25],
'_CG_rt1': [0.1],
'_CG_rt2': [0.25],
'_CG_rt3': [0.396],
'_AMT_CG_rt1': [0.1],
'_AMT_CG_rt2': [0.25],
'_AMT_CG_rt3': [0.396]

}
}

-28.1

So it does look like some sort of "AMT adjustment" might be required, or at least we should understand and document why this is happening. But it is not relevant for @codykallen's use case, so I agree with him that this should be merged.

Merging.

@MattHJensen MattHJensen merged commit e924989 into PSLmodels:master Oct 7, 2016
@feenberg
Copy link
Contributor

feenberg commented Oct 7, 2016

I wouldn't fool with the cg rates, rather I would zero out ltcg and add
that income to interest.

dan

On Fri, 7 Oct 2016, Matt Jensen wrote:

@martinholmer, could you help me understand this approach?

It seems to meet Cody’s pressing need — so that probably justifies pursuing it for now
— but won't CG_ec_rt only have the expected effect of reducing total tax liability
when CG_as_II is 1?

Another approach would be to design CG_ec_rt to work under any conditions.

If Cody wants the CG rates to match the II rates in his reform, then he can set them
that way manually. If we want users to be able to tax CG as II under current law (not
when there are only three brackets) then we can easily add more CG brackets.

The outstanding challenge then is to design CG_ec_rt to work under any conditions. The
reason it is difficult, I believe, is because of how CG and II are stacked on top of
each other. If we add the exclusion in the calculation of capital gains as Cody did in
his initial PR, then more income is taxed at ordinary income rates and total tax
liability goes up -- that is unexpected and not good. But couldn’t we just reduce
ordinary income by the right amount to offset that effect?

Maybe I am misunderstanding how your and Cody’s PRs work, though.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the
thread.[AHvQVV-iU5JSyCYXGNExzXgi6wgXeEZfks5qxoPIgaJpZM4KRQtl.gif]

@martinholmer
Copy link
Collaborator Author

Thanks for running all these tests before pull request #973 was merged by @MattHJensen, but the complexity of the tests put them way over my head. I've conducted a much more simple test to see if the new _CG_as_II parameter is doing the right thing. This test is simple enough for even me to understand. The simplicity is gained by doing two things: (1) making the II and CG rate schedules flat (at 20% for II income and 10% for CG income) and eliminating personal exemptions and the standard deduction; and (2) considering just one simple tax return: a sixty year-old single person with no dependents, no itemized deductions, and no income except for $100,000 in qualified dividends. When _CG_as_II is equal to zero (meaning that LTCG+QDIV income gets special tax treatment as under current law), this individual as an income tax liability of $10,000. But when the value of _CG_as_II is set equal to one (meaning that LTCG+QDIV income gets the same tax treatment as ordinary income), this individual as an income tax liability of $20,000. So it would seem as if the logic in pull request #973 is actually working in the way it was intended to work.

@MattHJensen @codykallen @feenberg @Amy-Xu @GoFroggyRun @andersonfrailey

Here are the details for those who are interested:

tax-calculator$ cat case.in
1 2015 0 1 0 6000 0 0 100000 0 0 0 0 0 0 0 0 0 0 0 0 0

tax-calculator$ cat base.json 
{
"_II_em":{"2015":[0]},
"_STD":{"2015":[[0, 0, 0, 0, 0, 0, 0]]},
"_II_rt1":{"2015":[0.20]},
"_II_rt2":{"2015":[0.20]},
"_II_rt3":{"2015":[0.20]},
"_II_rt4":{"2015":[0.20]},
"_II_rt5":{"2015":[0.20]},
"_II_rt6":{"2015":[0.20]},
"_II_rt7":{"2015":[0.20]},
"_II_rt8":{"2015":[0.20]},
"_CG_rt1":{"2015":[0.10]},
"_CG_rt2":{"2015":[0.10]},
"_CG_rt3":{"2015":[0.10]},
"_CG_rt4":{"2015":[0.10]}
}

tax-calculator$ cat same.json 
{
"_CG_as_II":{"2015":[1]},
"_II_em":{"2015":[0]},
"_STD":{"2015":[[0, 0, 0, 0, 0, 0, 0]]},
"_II_rt1":{"2015":[0.20]},
"_II_rt2":{"2015":[0.20]},
"_II_rt3":{"2015":[0.20]},
"_II_rt4":{"2015":[0.20]},
"_II_rt5":{"2015":[0.20]},
"_II_rt6":{"2015":[0.20]},
"_II_rt7":{"2015":[0.20]},
"_II_rt8":{"2015":[0.20]},
"_CG_rt1":{"2015":[0.10]},
"_CG_rt2":{"2015":[0.10]},
"_CG_rt3":{"2015":[0.10]},
"_CG_rt4":{"2015":[0.10]}
}

tax-calculator$ diff same.json base.json 
2d1
< "_CG_as_II":{"2015":[1]},

tax-calculator$ python simtax.py case.in --reform base.json

tax-calculator$ python simtax.py case.in --reform same.json

tax-calculator$ awk '{print FILENAME,$1,$18,$4}' case.in.out-simtax-*
case.in.out-simtax-base 1. 100000.00 10000.00
case.in.out-simtax-same 1. 100000.00 20000.00

So the individual has a $100,000 taxable income under both policy regimes, but in the "base" policy owes only $10,000 tax (because the CG flat rate is 10%) while in the "same" policy owes $20,000 tax (the same as if the income had been wages).

@feenberg
Copy link
Contributor

feenberg commented Oct 7, 2016

On Fri, 7 Oct 2016, Daniel Feenberg wrote:

I wouldn't fool with the cg rates, rather I would zero out ltcg and add that
income to interest.

Note that this avoids having to modify the AMT code also. But you have to
use the amount of gain after the loss limitation - capital gains in AGI,
not net gains.

dan

dan

On Fri, 7 Oct 2016, Matt Jensen wrote:

@martinholmer, could you help me understand this approach?

It seems to meet Cody’s pressing need — so that probably justifies
pursuing it for now
— but won't CG_ec_rt only have the expected effect of reducing total tax
liability
when CG_as_II is 1?

Another approach would be to design CG_ec_rt to work under any conditions.

If Cody wants the CG rates to match the II rates in his reform, then he
can set them
that way manually. If we want users to be able to tax CG as II under
current law (not
when there are only three brackets) then we can easily add more CG
brackets.

The outstanding challenge then is to design CG_ec_rt to work under any
conditions. The
reason it is difficult, I believe, is because of how CG and II are stacked
on top of
each other. If we add the exclusion in the calculation of capital gains as
Cody did in
his initial PR, then more income is taxed at ordinary income rates and
total tax
liability goes up -- that is unexpected and not good. But couldn’t we just
reduce
ordinary income by the right amount to offset that effect?

Maybe I am misunderstanding how your and Cody’s PRs work, though.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub, or mute the
thread.[AHvQVV-iU5JSyCYXGNExzXgi6wgXeEZfks5qxoPIgaJpZM4KRQtl.gif]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants