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

Updates to the tax treatment of intangibles #386

Merged
merged 31 commits into from
Apr 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
f4d8a4a
add two new parameters to allow different MTRs on deductions vs income
jdebacker Mar 21, 2024
b2dd3f5
add to u dict the mtrs on deductions
jdebacker Mar 21, 2024
7d5dbd3
add param for PV of ICT
jdebacker Mar 21, 2024
74a76bc
update CoC function withe parameters
jdebacker Mar 21, 2024
e20471f
update CoC eq'n in docs
jdebacker Mar 21, 2024
950dd25
change args in tests funcs
jdebacker Mar 21, 2024
111b439
change container for u deductions
jdebacker Mar 21, 2024
5c6f54d
fix test values for corrected eqn
jdebacker Mar 21, 2024
3e4d2f0
add income forecast method of depreciation
jdebacker Mar 21, 2024
c3b96b0
update expected outptu for model changes
jdebacker Mar 21, 2024
bb31352
test income foreast method
jdebacker Mar 26, 2024
82c0b20
use income forecast method for film and tv prod
jdebacker Apr 6, 2024
b4a2652
income forecast throughout
jdebacker Apr 6, 2024
569e02b
add R&E credit to default params
jdebacker Apr 10, 2024
b9252b7
add arg to coc to allow some variation by asset type
jdebacker Apr 10, 2024
03b207c
allow asset type to work
jdebacker Apr 17, 2024
8585a98
add lists of RE assets and industries
jdebacker Apr 18, 2024
3cfd20a
update to account for arrays
jdebacker Apr 18, 2024
3c4b05f
update ci versions
jdebacker Apr 21, 2024
88a8f1c
set default R&E credit amount to that of CBO
jdebacker Apr 21, 2024
46ac014
condition R&E on industry
jdebacker Apr 21, 2024
38a7ba2
update results for new params
jdebacker Apr 21, 2024
6fa953e
Merge remote-tracking branch 'upstream/master' into ip1
jdebacker Apr 21, 2024
cb2c1f8
fix initialized values of inv tax credit
jdebacker Apr 22, 2024
1b7aefd
new test to catch if statement
jdebacker Apr 22, 2024
d4b7bdf
format
jdebacker Apr 22, 2024
e77fb9b
updates for 2034
jdebacker Apr 22, 2024
6db6578
update calc functions to take dict for re_credit
jdebacker Apr 22, 2024
8c91c8f
re credit dict created in parameters
jdebacker Apr 22, 2024
ed8d080
update default with re_credit for all years
jdebacker Apr 22, 2024
9b0415a
update docs for R&E credit
jdebacker Apr 22, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v4
with:
persist-credentials: false

Expand All @@ -36,10 +36,12 @@ jobs:
run: |
pytest -m 'not needs_puf' --cov=./ --cov-report=xml
- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
if: matrix.os == 'ubuntu-latest'
uses: codecov/codecov-action@v4
with:
token: ${{ secrets.CODECOV_TOKEN }} # not required for public repos
file: ./coverage.xml
flags: unittests
name: codecov-umbrella
fail_ci_if_error: true
verbose: true
verbose: true
115 changes: 110 additions & 5 deletions ccc/calcfunctions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import numpy as np
import pandas as pd
from ccc.constants import TAX_METHODS
from ccc.constants import TAX_METHODS, RE_ASSETS, RE_INDUSTRIES
from ccc.utils import str_modified

pd.set_option("future.no_silent_downcasting", True)
Expand Down Expand Up @@ -140,6 +140,49 @@ def econ(delta, bonus, r, pi):
return z


def income_forecast(Y, delta, bonus, r):
r"""
Makes the calculation for the income forecast method.

The income forecast method involved deducting expenses in relation
to forecasted income over the next 10 years. CCC follows the CBO
methodology (CBO, 2018:
https://www.cbo.gov/system/files/2018-11/54648-Intangible_Assets.pdf)
and approximate this method with the DBSL method, but with a the "b"
factor determined by economic depreciation rates.

.. math::
z = \frac{\beta}{\beta+r}\left[1-e^{-(\beta+r)Y^{*}}\right]+
\frac{e^{-\beta Y^{*}}}{(Y-Y^{*})r}
\left[e^{-rY^{*}}-e^{-rY}\right]

Args:
Y (array_like): asset life in years
delta (array_like): rate of economic depreciation
bonus (array_like): rate of bonus depreciation
r (scalar): discount rate

Returns:
z (array_like): net present value of depreciation deductions for
$1 of investment

"""
b = 10 * delta
beta = b / Y
Y_star = Y * (1 - (1 / b))
z = bonus + (
(1 - bonus)
* (
((beta / (beta + r)) * (1 - np.exp(-1 * (beta + r) * Y_star)))
+ (
(np.exp(-1 * beta * Y_star) / ((Y - Y_star) * r))
* (np.exp(-1 * r * Y_star) - np.exp(-1 * r * Y))
)
)
)
return z


def npv_tax_depr(df, r, pi, land_expensing):
"""
Depending on the method of depreciation, makes calls to either
Expand All @@ -164,6 +207,10 @@ def npv_tax_depr(df, r, pi, land_expensing):
df.loc[idx, "z"] = sl(df.loc[idx, "Y"], df.loc[idx, "bonus"], r)
idx = df["method"] == "Economic"
df.loc[idx, "z"] = econ(df.loc[idx, "delta"], df.loc[idx, "bonus"], r, pi)
idx = df["method"] == "Income Forecast"
df.loc[idx, "z"] = income_forecast(
df.loc[idx, "Y"], df.loc[idx, "delta"], df.loc[idx, "bonus"], r
)
idx = df["method"] == "Expensing"
df.loc[idx, "z"] = 1.0
idx = df["asset_name"] == "Land"
Expand All @@ -175,30 +222,88 @@ def npv_tax_depr(df, r, pi, land_expensing):
return z


def eq_coc(delta, z, w, u, inv_tax_credit, pi, r):
def eq_coc(
delta,
z,
w,
u,
u_d,
inv_tax_credit,
psi,
nu,
pi,
r,
re_credit=None,
asset_code=None,
ind_code=None,
):
r"""
Compute the cost of capital

.. math::
\rho = \frac{(r-\pi+\delta)}{1-u}(1-uz)+w-\delta
\rho = \frac{(r-\pi+\delta)}{1-u}(1-u_dz(1-\psi k) - k\nu)+w-\delta

Args:
delta (array_like): rate of economic depreciation
z (array_like): net present value of depreciation deductions for
$1 of investment
w (scalar): property tax rate
u (scalar): statutory marginal tax rate for the first layer of
u (scalar): marginal tax rate for the first layer of
income taxes
u_d (scalar): marginal tax rate on deductions
inv_tax_credit (scalar): investment tax credit rate
psi (scalar): fraction investment tax credit that affects
depreciable basis of the investment
nu (scalar): NPV of the investment tax credit
pi (scalar): inflation rate
r (scalar): discount rate
re_credit (dict): rate of R&E credit by asset or industry
asset_code (array_like): asset code
ind_code (array_like): industry code

Returns:
rho (array_like): the cost of capital

"""
# Initialize re_credit_rate (only needed if arrays are passed in --
# if not, can include the R&E credit in the inv_tax_credit object)
if isinstance(delta, np.ndarray):
re_credit_rate_ind = np.zeros_like(delta)
re_credit_rate_asset = np.zeros_like(delta)
# Update by R&E credit rate amounts by industry
if (ind_code is not None) and (re_credit is not None):
idx = [
index
for index, element in enumerate(ind_code)
if element in re_credit["By industry"].keys()
]
print("Keys = ", re_credit["By industry"].keys())
print("Ind idx = ", idx)
print("Dict = ", re_credit["By industry"], re_credit)
ind_code_idx = [ind_code[i] for i in idx]
re_credit_rate_ind[idx] = [
re_credit["By industry"][ic] for ic in ind_code_idx
]
# Update by R&E credit rate amounts by asset
if (asset_code is not None) and (re_credit is not None):
idx = [
index
for index, element in enumerate(asset_code)
if element in re_credit["By asset"].keys()
]
asset_code_idx = [asset_code[i] for i in idx]
re_credit_rate_asset[idx] = [
re_credit["By asset"][ac] for ac in asset_code_idx
]
# take the larger of the two R&E credit rates
inv_tax_credit += np.maximum(re_credit_rate_asset, re_credit_rate_ind)
print("RE_credit object =", re_credit)
print("inv_tax_credit object =", inv_tax_credit)
rho = (
((r - pi + delta) / (1 - u)) * (1 - inv_tax_credit - u * z) + w - delta
((r - pi + delta) / (1 - u))
* (1 - inv_tax_credit * nu - u_d * z * (1 - psi * inv_tax_credit))
+ w
- delta
)

return rho
Expand Down
6 changes: 6 additions & 0 deletions ccc/calculator.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,15 @@ def calc_base(self):
dfs[t]["z_" + str(f)],
self.__p.property_tax,
self.__p.u[t],
self.__p.u_d[t],
self.__p.inv_tax_credit,
self.__p.psi,
self.__p.nu,
self.__p.inflation_rate,
self.__p.r[t][f],
self.__p.re_credit,
dfs[t]["bea_asset_code"],
dfs[t]["bea_ind_code"],
)
if not self.__p.inventory_expensing:
idx = dfs[t]["asset_name"] == "Inventories"
Expand Down
13 changes: 13 additions & 0 deletions ccc/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@

OUTPUT_DATA_FORMATS = ["csv", "tex", "excel", "json", "html", None]

# TODO: perhaps make as a dict so that can vary across years?
# And if policy variant, maybe move to default params?
RE_ASSETS = [
"ENS3", # "Credit-eligible own account software",
"RD70", # "Credit-eligible research and development",
"SU60", # "Wind and solar power structures",
]

RE_INDUSTRIES = [
"3340", # "Computer_and_Electronic_Product_Manufacturing",
]

MAJOR_IND_ORDERED = [
"Agriculture, forestry, fishing, and hunting",
"Mining",
Expand Down Expand Up @@ -48,6 +60,7 @@
"SL": 1.0,
"Economic": 1.0,
"Expensing": 1.0,
"Income Forecast": 1.0,
}

MINOR_ASSET_GROUPS = dict.fromkeys(
Expand Down
Loading
Loading